@cortexkit/anthropic-auth-core 2.0.2 → 2.1.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/accounts.d.ts +7 -1
- package/dist/cache1h.d.ts +2 -0
- package/dist/cachekeep-registry.d.ts +3 -0
- package/dist/cachekeep.d.ts +2 -0
- package/dist/cch.d.ts +19 -0
- package/dist/claude-code.d.ts +8 -2
- package/dist/claustrum-enrollment.d.ts +5 -1
- package/dist/constants.d.ts +15 -3
- package/dist/dump.d.ts +4 -0
- package/dist/fast.d.ts +2 -0
- package/dist/index.d.ts +15 -8
- package/dist/index.js +29647 -25296
- package/dist/insula-quota.d.ts +25 -0
- package/dist/internal-types/auth-menu/terminal.d.ts +43 -0
- package/dist/internal-types/claustrum/availability.d.ts +89 -0
- package/dist/internal-types/claustrum/consumer.d.ts +172 -0
- package/dist/internal-types/claustrum/custody.d.ts +299 -0
- package/dist/internal-types/claustrum/enrollment.d.ts +164 -0
- package/dist/internal-types/claustrum/errors.d.ts +17 -0
- package/dist/internal-types/claustrum/host-slot.d.ts +39 -0
- package/dist/internal-types/claustrum/index.d.ts +21 -0
- package/dist/internal-types/claustrum/insula.d.ts +117 -0
- package/dist/internal-types/claustrum/interlock.d.ts +27 -0
- package/dist/internal-types/claustrum/roster.d.ts +221 -0
- package/dist/internal-types/claustrum/watch.d.ts +141 -0
- package/dist/internal-types/fs/refresh-file-lock.d.ts +8 -2
- package/dist/internal-types/quota/format.d.ts +43 -3
- package/dist/internal-types/quota/index.d.ts +3 -3
- package/dist/internal-types/quota/projection.d.ts +16 -0
- package/dist/internal-types/routing/admission.d.ts +93 -0
- package/dist/internal-types/routing/index.d.ts +8 -0
- package/dist/internal-types/routing/ordered.d.ts +47 -0
- package/dist/internal-types/routing/pins.d.ts +22 -0
- package/dist/internal-types/routing/sticky.d.ts +158 -0
- package/dist/internal-types/setup/command-error.d.ts +32 -0
- package/dist/internal-types/setup/command.d.ts +3 -0
- package/dist/internal-types/setup/detect.d.ts +6 -0
- package/dist/internal-types/setup/index.d.ts +6 -0
- package/dist/internal-types/setup/process-fence.d.ts +43 -0
- package/dist/internal-types/setup/register.d.ts +11 -0
- package/dist/internal-types/setup/types.d.ts +150 -0
- package/dist/internal-types/store/pool.d.ts +1 -1
- package/dist/internal-types/store/refresh-lock.d.ts +0 -1
- package/dist/logging.d.ts +2 -0
- package/dist/mid-conversation-output-config.d.ts +34 -2
- package/dist/migration-source.d.ts +2 -0
- package/dist/native-account-runtime.d.ts +9 -4
- package/dist/native-account-view.d.ts +19 -3
- package/dist/native-custody-activation.d.ts +1 -1
- package/dist/native-custody.d.ts +130 -0
- package/dist/native-enrollment-authority.d.ts +5 -1
- package/dist/native-host-auth-read.d.ts +2 -1
- package/dist/native-host-auth-write.d.ts +2 -0
- package/dist/native-menu-executor.d.ts +1 -1
- package/dist/native-migration.d.ts +25 -4
- package/dist/native-quota-observations.d.ts +26 -0
- package/dist/native-runtime.d.ts +5 -1
- package/dist/native-transient-account.d.ts +3 -4
- package/dist/native-ui.d.ts +2 -0
- package/dist/native-usage-publication.d.ts +5 -0
- package/dist/native-vault-invalidation.d.ts +9 -0
- package/dist/native-vault-runtime.d.ts +36 -3
- package/dist/network-errors.d.ts +1 -0
- package/dist/owner-only.d.ts +19 -0
- package/dist/pool-authority.d.ts +27 -8
- package/dist/pool-paths.d.ts +2 -0
- package/dist/provider-error.d.ts +34 -0
- package/dist/publication-rename.d.ts +11 -0
- package/dist/quota-manager.d.ts +21 -4
- package/dist/quotas.d.ts +31 -2
- package/dist/relay.d.ts +20 -0
- package/dist/request-gzip.d.ts +99 -0
- package/package.json +4 -4
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { ClaustrumLogger } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* How a host currently learns about changes, for logs and status displays.
|
|
4
|
+
* `watching`: a watch was accepted and is open, so polling is reduced to a
|
|
5
|
+
* slow safety read. `polling`: no watch is open yet (not started, opening, or
|
|
6
|
+
* re-subscribing after a close), so the host polls at its usual interval.
|
|
7
|
+
* `fallback`: the watch is unavailable (an older module or client, a refusal,
|
|
8
|
+
* the watch limit, or repeated failures), so the host polls at its usual
|
|
9
|
+
* interval and may try a watch again later. Never carries secrets.
|
|
10
|
+
*/
|
|
11
|
+
export type WatchMode = 'watching' | 'polling' | 'fallback';
|
|
12
|
+
/**
|
|
13
|
+
* While a vault watch is healthy, the roster is still read this often: the
|
|
14
|
+
* watch carries no history, so a lost notice or a silently dead stream is
|
|
15
|
+
* caught within this period.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SAFETY_POLL_MS: number;
|
|
18
|
+
/**
|
|
19
|
+
* After a watch fails without being accepted, the host polls and tries one
|
|
20
|
+
* new watch this much later, so a refusing or draining module is never asked
|
|
21
|
+
* in a tight loop.
|
|
22
|
+
*/
|
|
23
|
+
export declare const WATCH_RETRY_MS: number;
|
|
24
|
+
/**
|
|
25
|
+
* First wait before re-subscribing a watch that closed after it had been
|
|
26
|
+
* accepted. It doubles for each further close in a row, up to WATCH_RETRY_MS.
|
|
27
|
+
*/
|
|
28
|
+
export declare const RESUBSCRIBE_BACKOFF_MS = 1000;
|
|
29
|
+
/**
|
|
30
|
+
* The fields every change notice carries, from the vault's watchScoped and
|
|
31
|
+
* insula's usage.watch alike.
|
|
32
|
+
*/
|
|
33
|
+
export interface ChangeNotice {
|
|
34
|
+
readonly kind: string;
|
|
35
|
+
readonly epoch: string;
|
|
36
|
+
readonly seq: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Tells whether a notice follows the previous one with nothing lost between
|
|
40
|
+
* them. `seq` restarts at 1 for every watch and `epoch` names the sending
|
|
41
|
+
* module's process lifetime. A sequence gap (notices are sheddable), a
|
|
42
|
+
* different epoch (the module restarted) or a notice of an unknown kind all
|
|
43
|
+
* mean changes may have been missed, so the caller must re-read everything.
|
|
44
|
+
*/
|
|
45
|
+
export declare class ChangeNoticeReader {
|
|
46
|
+
#private;
|
|
47
|
+
/**
|
|
48
|
+
* Begin a new watch, whose first notice is seq 1 again. The epoch is kept,
|
|
49
|
+
* so a module restart between two watches still reads as a break.
|
|
50
|
+
*/
|
|
51
|
+
restart(): void;
|
|
52
|
+
continuous(notice: ChangeNotice): boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Runs `read` with at most one call in flight and at most one more queued
|
|
56
|
+
* behind it, however many requests arrive meanwhile. A request made while a
|
|
57
|
+
* read is running may have arrived after that read took its snapshot, so it
|
|
58
|
+
* earns exactly one more read; any further requests share that one.
|
|
59
|
+
*/
|
|
60
|
+
export declare class CoalescedRead {
|
|
61
|
+
#private;
|
|
62
|
+
constructor(read: () => Promise<void>, onError: (error: unknown) => void);
|
|
63
|
+
/** The read in flight (including its queued follow-up), if any. */
|
|
64
|
+
get running(): Promise<void> | undefined;
|
|
65
|
+
request(): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
/** A vault watch notice: `view` is the same digest `list_scoped` returns. */
|
|
68
|
+
export interface ScopedWatchNoticeShape extends ChangeNotice {
|
|
69
|
+
readonly view: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* How a vault watch ended, as claustrum-client 0.12.0 reports it. `refused`
|
|
73
|
+
* and `not_accepting` both mean the watch closed before its first notice; the
|
|
74
|
+
* client then made one `list_scoped` read to learn why, and `refused` carries
|
|
75
|
+
* that read's refusal.
|
|
76
|
+
*/
|
|
77
|
+
export type ScopedWatchEnd = {
|
|
78
|
+
readonly kind: 'refused';
|
|
79
|
+
readonly error: unknown;
|
|
80
|
+
} | {
|
|
81
|
+
readonly kind: 'transport_lost';
|
|
82
|
+
readonly error: unknown;
|
|
83
|
+
} | {
|
|
84
|
+
readonly kind: 'ended' | 'not_accepting' | 'unsupported' | 'stopped';
|
|
85
|
+
};
|
|
86
|
+
export interface ScopedWatchHandle {
|
|
87
|
+
stop(): void;
|
|
88
|
+
readonly closed: Promise<ScopedWatchEnd>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The part of a vault client that can watch. claustrum-client added
|
|
92
|
+
* `watchScoped` in 0.12.0; the peer range still admits 0.6.2, so it is
|
|
93
|
+
* optional and detected at run time.
|
|
94
|
+
*/
|
|
95
|
+
export interface ScopedWatchSource {
|
|
96
|
+
watchScoped?(onNotice: (notice: ScopedWatchNoticeShape) => void, options?: {
|
|
97
|
+
enrollmentToken?: string;
|
|
98
|
+
}): ScopedWatchHandle;
|
|
99
|
+
}
|
|
100
|
+
export interface ScopedRosterWatchOptions {
|
|
101
|
+
client: ScopedWatchSource;
|
|
102
|
+
/** The enrollment token, read again for every watch so a new one is used. */
|
|
103
|
+
enrollmentToken: () => Promise<string>;
|
|
104
|
+
/** The view of the roster this host last discovered; undefined for none. */
|
|
105
|
+
view: () => string | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* The host's existing discovery: `list_scoped`, the roster commit and
|
|
108
|
+
* everything that follows it. The watch never reads the vault list itself.
|
|
109
|
+
* `view` is the view the newest notice carried.
|
|
110
|
+
*/
|
|
111
|
+
discover: (view: string) => Promise<unknown>;
|
|
112
|
+
/**
|
|
113
|
+
* The vault refused the watch, and the confirming `list_scoped` read it
|
|
114
|
+
* with this error (a revoked enrollment ends both with `not_found`). Handle
|
|
115
|
+
* it as discovery handles the same refusal from `list_scoped`.
|
|
116
|
+
*/
|
|
117
|
+
onRefused: (error: unknown) => void;
|
|
118
|
+
onTransportLost?: () => void;
|
|
119
|
+
onReachable?: () => void;
|
|
120
|
+
onError?: (error: unknown) => void;
|
|
121
|
+
onMode?: (mode: WatchMode) => void;
|
|
122
|
+
logger?: ClaustrumLogger;
|
|
123
|
+
now?: () => number;
|
|
124
|
+
setTimeoutImpl?: typeof setTimeout;
|
|
125
|
+
clearTimeoutImpl?: typeof clearTimeout;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Keeps one vault `watchScoped` open for a host and runs the host's discovery
|
|
129
|
+
* when a notice shows the visible account list changed. Discovery runs only
|
|
130
|
+
* when the notice's view differs from the last discovered roster's view, when
|
|
131
|
+
* a notice shows a gap or a new epoch, or on the first notice of a watch that
|
|
132
|
+
* was re-subscribed after a close (the time between two watches was not
|
|
133
|
+
* observed). It never opens a second watch while one is open.
|
|
134
|
+
*/
|
|
135
|
+
export declare class ScopedRosterWatch {
|
|
136
|
+
#private;
|
|
137
|
+
constructor(options: ScopedRosterWatchOptions);
|
|
138
|
+
get mode(): WatchMode;
|
|
139
|
+
start(): void;
|
|
140
|
+
stop(): void;
|
|
141
|
+
}
|
|
@@ -13,7 +13,7 @@ export interface RefreshFileLock {
|
|
|
13
13
|
whenLost(): Promise<LockLoss>;
|
|
14
14
|
hasLost(): boolean;
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
type RefreshFileLockOptions = {
|
|
17
17
|
name: string;
|
|
18
18
|
ttlMs: number;
|
|
19
19
|
/**
|
|
@@ -22,7 +22,6 @@ export declare function acquireRefreshFileLock(options: {
|
|
|
22
22
|
* be one host's path.
|
|
23
23
|
*/
|
|
24
24
|
path: string;
|
|
25
|
-
now?: () => number;
|
|
26
25
|
renew?: boolean;
|
|
27
26
|
renewIntervalMs?: number;
|
|
28
27
|
/** Notifies a live-owner refusal without awaiting; observer failures are ignored. */
|
|
@@ -30,4 +29,11 @@ export declare function acquireRefreshFileLock(options: {
|
|
|
30
29
|
/** Synchronous, never awaited; throws and returned promise/thenable rejections are isolated. */
|
|
31
30
|
onRenewalTimer?: (event: 'scheduled' | 'cancelled') => void;
|
|
32
31
|
onStep?: (step: 'stale-marker-stat' | 'stale-marker-claimed' | 'stale-lock-observed' | 'lock-read-ready' | 'lock-missing' | 'stale-lock-confirmed' | 'stale-lock-removed' | 'stale-lock-recreated' | 'eviction-marker-acquired' | 'renewal-owner-confirmed' | 'renewal-marker-unavailable' | 'renewal-write-fenced' | 'renewal-write-ready' | 'renewal-write-finished' | 'relinquish-read' | 'renewal-finished' | 'release-owner-confirmed') => void | Promise<void>;
|
|
32
|
+
};
|
|
33
|
+
/** Filesystem leases share wall time across all callers and processes. */
|
|
34
|
+
export declare function acquireRefreshFileLock(options: RefreshFileLockOptions): Promise<RefreshFileLock | null>;
|
|
35
|
+
/** @internal Clock seam for lease tests; not exported by the package's fs entry point. */
|
|
36
|
+
export declare function acquireRefreshFileLockWithClock(options: RefreshFileLockOptions & {
|
|
37
|
+
now?: () => number;
|
|
33
38
|
}): Promise<RefreshFileLock | null>;
|
|
39
|
+
export {};
|
|
@@ -11,8 +11,14 @@ export declare const NO_LIMITS_REPORTED = "no limits reported";
|
|
|
11
11
|
/**
|
|
12
12
|
* `compact` fits a footer or a sidebar row: each window's figure, the credit
|
|
13
13
|
* budget and the stale marker, without reset times. `full` adds each reset.
|
|
14
|
+
* `narrow` fits one line per account in a narrow sidebar: `5h 92% · 7d 66%
|
|
15
|
+
* · Fa 98% left`, the account-wide windows plus at most one model-scoped
|
|
16
|
+
* window or the budget (as `cr`), and no stale marker (see `isQuotaStale`).
|
|
17
|
+
* A reading whose windows belong to named groups and that has no
|
|
18
|
+
* account-wide window reads up to two groups instead:
|
|
19
|
+
* `Ge 7d 62% · Ot 7d 48% left`.
|
|
14
20
|
*/
|
|
15
|
-
export type QuotaTextForm = 'compact' | 'full';
|
|
21
|
+
export type QuotaTextForm = 'compact' | 'full' | 'narrow';
|
|
16
22
|
export interface QuotaTextOptions {
|
|
17
23
|
/** The current time in ms, for reset countdowns and the stale marker. */
|
|
18
24
|
now: number;
|
|
@@ -21,6 +27,26 @@ export interface QuotaTextOptions {
|
|
|
21
27
|
/** Defaults to `QUOTA_STALE_AFTER_MS`. */
|
|
22
28
|
staleAfterMs?: number;
|
|
23
29
|
}
|
|
30
|
+
export interface QuotaWindowPart {
|
|
31
|
+
kind: 'window' | 'budget';
|
|
32
|
+
/** The display name of a quota window, or `credits` for the budget. */
|
|
33
|
+
name: string;
|
|
34
|
+
/** The shared figure wording, including `credits` for budget figures. */
|
|
35
|
+
figure: string;
|
|
36
|
+
/** The unrounded remaining percentage, clamped to 0–100, when known. */
|
|
37
|
+
remainingPercent?: number;
|
|
38
|
+
/** A bare relative reset span such as `4d`, only when the reset is future. */
|
|
39
|
+
resetIn?: string;
|
|
40
|
+
checkedAt: number;
|
|
41
|
+
/** Whether the reset followed this reading and has arrived by `now`. */
|
|
42
|
+
resetSinceReading: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface QuotaParts {
|
|
45
|
+
/** Reported windows in display order, followed by the budget when present. */
|
|
46
|
+
windows: QuotaWindowPart[];
|
|
47
|
+
/** The shared checked-age wording, present only when the reading is stale. */
|
|
48
|
+
age?: string;
|
|
49
|
+
}
|
|
24
50
|
/**
|
|
25
51
|
* The name a window is shown under. A general window (scope `all`) is named
|
|
26
52
|
* by its stored length; a model-scoped window keeps the label the plugin
|
|
@@ -29,17 +55,31 @@ export interface QuotaTextOptions {
|
|
|
29
55
|
* were recorded) falls back to its label.
|
|
30
56
|
*/
|
|
31
57
|
export declare function quotaWindowName(limit: Pick<ProjectedLimit, 'scope' | 'label' | 'windowMinutes'>): string;
|
|
58
|
+
/**
|
|
59
|
+
* Structured quota wording for layouts that need one row per window. The
|
|
60
|
+
* windows use the same filtering and order as `quotaTextParts`; future resets
|
|
61
|
+
* and the optional age keep the shared formatter's wording.
|
|
62
|
+
*/
|
|
63
|
+
export declare function quotaWindowParts(quota: ProjectedQuota | undefined, options: QuotaTextOptions): QuotaParts;
|
|
64
|
+
/**
|
|
65
|
+
* Whether the reading is stale: true exactly when the compact and full forms
|
|
66
|
+
* would end with `checked … ago`. The narrow form leaves that age out to stay
|
|
67
|
+
* short, so a caller drawing a narrow line uses this to dim it instead.
|
|
68
|
+
*/
|
|
69
|
+
export declare function isQuotaStale(quota: ProjectedQuota | undefined, options: QuotaTextOptions): boolean;
|
|
32
70
|
/**
|
|
33
71
|
* The pieces of a quota text, in order: one per reported window
|
|
34
72
|
* (`5h 58% left, resets 2h`), then the credit budget when there is one, then
|
|
35
73
|
* `checked 3h ago` when the oldest piece shown is stale. Empty when nothing
|
|
36
74
|
* was reported; `formatQuota` turns that into a sentence. A caller printing
|
|
37
|
-
* one line per window (the terminal auth menu) uses these directly.
|
|
75
|
+
* one line per window (the terminal auth menu) uses these directly. In the
|
|
76
|
+
* narrow form the pieces are the narrow line's (`5h 92%`, `7d 66% left`).
|
|
38
77
|
*/
|
|
39
78
|
export declare function quotaTextParts(quota: ProjectedQuota | undefined, options: QuotaTextOptions): string[];
|
|
40
79
|
/**
|
|
41
80
|
* A row's quota as one line: `5h 58% left · 7d 90% left` in the compact
|
|
42
|
-
* form, with `, resets 2h` after each window in the full form
|
|
81
|
+
* form, with `, resets 2h` after each window in the full form, and
|
|
82
|
+
* `5h 58% · 7d 90% left` in the narrow form. With nothing
|
|
43
83
|
* reported it is `NO_QUOTA_READING`, or `NO_LIMITS_REPORTED` when the
|
|
44
84
|
* provider answered without a limit for any window.
|
|
45
85
|
*/
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export type { QuotaCodec } from './codec.js';
|
|
2
2
|
export { quotaCodec } from './codec.js';
|
|
3
|
-
export type { QuotaTextForm, QuotaTextOptions } from './format.js';
|
|
4
|
-
export { formatQuota, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, } from './format.js';
|
|
3
|
+
export type { QuotaParts, QuotaTextForm, QuotaTextOptions, QuotaWindowPart, } from './format.js';
|
|
4
|
+
export { formatQuota, isQuotaStale, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, quotaWindowParts, } from './format.js';
|
|
5
5
|
export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaCounter, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
|
|
6
6
|
export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaMap, } from './map.js';
|
|
7
7
|
export type { ObservedBudget, ObservedPair, ObservedReading, QuotaObservation, } from './merge.js';
|
|
8
8
|
export { isQuotaObservation, mergeQuotaObservation, QuotaCodecError, } from './merge.js';
|
|
9
|
-
export type { ExhaustionReset, ProjectedBudget, ProjectedLimit, ProjectedQuota, } from './projection.js';
|
|
9
|
+
export type { ExhaustionReset, ProjectedBudget, ProjectedLimit, ProjectedQuota, QuotaGroup, } from './projection.js';
|
|
10
10
|
export { budgetExhaustedResetAt, futureResetAt, projectQuota, readsExhausted, } from './projection.js';
|
|
@@ -19,6 +19,15 @@ export interface ProjectedBudget {
|
|
|
19
19
|
remainingPercent?: number;
|
|
20
20
|
resetsAt?: string;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* A named group of windows, such as antigravity-auth's `Gemini` and
|
|
24
|
+
* `Other models`: every window stored under `scope` belongs to it, and
|
|
25
|
+
* `label` is the group's display name.
|
|
26
|
+
*/
|
|
27
|
+
export interface QuotaGroup {
|
|
28
|
+
scope: string;
|
|
29
|
+
label: string;
|
|
30
|
+
}
|
|
22
31
|
export interface ProjectedQuota {
|
|
23
32
|
scope: string;
|
|
24
33
|
/** One entry per label: longest known window first, unknown lengths last. */
|
|
@@ -31,6 +40,13 @@ export interface ProjectedQuota {
|
|
|
31
40
|
/** Present only when the stored budget is a reading, not cleared. */
|
|
32
41
|
budget?: ProjectedBudget;
|
|
33
42
|
counters?: Record<string, QuotaCounter>;
|
|
43
|
+
/**
|
|
44
|
+
* The named groups the windows belong to, in the order the plugin lists
|
|
45
|
+
* them. Set only by a plugin whose windows all belong to such groups; the
|
|
46
|
+
* narrow form then shows one item per group (see `formatQuota`).
|
|
47
|
+
* `projectQuota` never sets it.
|
|
48
|
+
*/
|
|
49
|
+
groups?: readonly QuotaGroup[];
|
|
34
50
|
}
|
|
35
51
|
/**
|
|
36
52
|
* Resolves `map` for a request in `scope` (`all` or a model family). A family
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type QuotaMap } from '../quota/map.js';
|
|
2
|
+
import { type ProjectedQuota } from '../quota/projection.js';
|
|
3
|
+
export type RowKind = 'oauth' | 'api-key';
|
|
4
|
+
export interface RoutingRow {
|
|
5
|
+
id: string;
|
|
6
|
+
kind: RowKind;
|
|
7
|
+
/** The row's stored quota map; absent is the same as an empty map. */
|
|
8
|
+
quota?: QuotaMap;
|
|
9
|
+
}
|
|
10
|
+
export interface WindowRef {
|
|
11
|
+
scope: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ExclusionInputs {
|
|
15
|
+
/** Row id to the time (ms) a rate-limit mark expires. */
|
|
16
|
+
rateLimitMarks?: ReadonlyMap<string, number>;
|
|
17
|
+
/** Row id to the time (ms) a failed refresh may be retried. */
|
|
18
|
+
refreshBackoff?: ReadonlyMap<string, number>;
|
|
19
|
+
}
|
|
20
|
+
export interface AdmissionInput extends ExclusionInputs {
|
|
21
|
+
rows: readonly RoutingRow[];
|
|
22
|
+
/** `all` (the default) or a model family. */
|
|
23
|
+
scope?: string;
|
|
24
|
+
/** Labels the scope requires an entry for; defaults to `['primary']`. */
|
|
25
|
+
requiredLabels?: readonly string[];
|
|
26
|
+
now: number;
|
|
27
|
+
/**
|
|
28
|
+
* Called synchronously, once per refusal at gates 2 to 4, with the row id.
|
|
29
|
+
* Its return value is ignored and never awaited, so a pull the caller
|
|
30
|
+
* starts from here cannot delay the refusal.
|
|
31
|
+
*/
|
|
32
|
+
requestPull?: (rowId: string) => void;
|
|
33
|
+
}
|
|
34
|
+
export type AdmissionRefusal = {
|
|
35
|
+
id: string;
|
|
36
|
+
stage: 1;
|
|
37
|
+
gate: 2;
|
|
38
|
+
reason: 'needs-first-reading';
|
|
39
|
+
pullRequested: true;
|
|
40
|
+
} | {
|
|
41
|
+
id: string;
|
|
42
|
+
stage: 1;
|
|
43
|
+
gate: 3;
|
|
44
|
+
reason: 'unknown-window';
|
|
45
|
+
window: WindowRef;
|
|
46
|
+
pullRequested: true;
|
|
47
|
+
} | {
|
|
48
|
+
id: string;
|
|
49
|
+
stage: 1;
|
|
50
|
+
gate: 4;
|
|
51
|
+
reason: 'unknown-reset';
|
|
52
|
+
window: WindowRef;
|
|
53
|
+
pullRequested: true;
|
|
54
|
+
} | {
|
|
55
|
+
id: string;
|
|
56
|
+
stage: 1;
|
|
57
|
+
gate: 5;
|
|
58
|
+
reason: 'exhausted';
|
|
59
|
+
window: WindowRef;
|
|
60
|
+
resetsAt: string;
|
|
61
|
+
resetAtMs: number;
|
|
62
|
+
} | {
|
|
63
|
+
id: string;
|
|
64
|
+
stage: 2;
|
|
65
|
+
reason: 'budget-spent';
|
|
66
|
+
resetsAt: string;
|
|
67
|
+
resetAtMs: number;
|
|
68
|
+
};
|
|
69
|
+
export interface AdmissionExclusion {
|
|
70
|
+
id: string;
|
|
71
|
+
reason: 'rate-limited' | 'refresh-backoff';
|
|
72
|
+
until: number;
|
|
73
|
+
}
|
|
74
|
+
export interface AdmittedRow {
|
|
75
|
+
id: string;
|
|
76
|
+
kind: RowKind;
|
|
77
|
+
/** The projection admission judged; absent for API-key rows. */
|
|
78
|
+
projection?: ProjectedQuota;
|
|
79
|
+
/** Set when a spent budget was kept because no other path survived. */
|
|
80
|
+
lastPath?: true;
|
|
81
|
+
}
|
|
82
|
+
export interface AdmissionResult {
|
|
83
|
+
/** In input order. */
|
|
84
|
+
admitted: AdmittedRow[];
|
|
85
|
+
refused: AdmissionRefusal[];
|
|
86
|
+
excluded: AdmissionExclusion[];
|
|
87
|
+
/** Ids a pull was requested for, in input order. */
|
|
88
|
+
pulls: string[];
|
|
89
|
+
}
|
|
90
|
+
/** The exclusion that applies to `id` at `now`, if any. */
|
|
91
|
+
export declare function exclusionFor(id: string, inputs: ExclusionInputs, now: number): AdmissionExclusion | undefined;
|
|
92
|
+
/** Runs both admission stages over `input.rows`. */
|
|
93
|
+
export declare function admit(input: AdmissionInput): AdmissionResult;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { AdmissionExclusion, AdmissionInput, AdmissionRefusal, AdmissionResult, AdmittedRow, ExclusionInputs, RoutingRow, RowKind, WindowRef, } from './admission.js';
|
|
2
|
+
export { admit, exclusionFor } from './admission.js';
|
|
3
|
+
export type { OrderedAttempt, OrderedPlacement, OrderedRoute, OrderedRouteInput, ResolvedRoutingMode, RoutingMode, } from './ordered.js';
|
|
4
|
+
export { DEFAULT_FORMER_MAIN_ID, nextOrderedAttempt, orderForPlacement, resolveRoutingMode, routeOrdered, } from './ordered.js';
|
|
5
|
+
export type { StickyPin } from './pins.js';
|
|
6
|
+
export { isPinValid, pendingBytesForPins } from './pins.js';
|
|
7
|
+
export type { PinAction, StickyBreakDecision, StickyRoute, StickyRouteInput, StickySelection, StickySelectionCandidate, StickySelectionInput, StickyStatusClass, StickyStatusClassifier, } from './sticky.js';
|
|
8
|
+
export { decideStickyBreak, defaultStickyStatusClass, MIN_RESET_HOURS, MIN_WEIGHT, QUOTA_STALENESS_MS, routeSticky, selectStickyCandidate, snapshotCheckedAt, sustainableWindowWeight, } from './sticky.js';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type AdmissionInput, type AdmissionResult } from './admission.js';
|
|
2
|
+
export type RoutingMode = 'ordered' | 'sticky-balanced';
|
|
3
|
+
/** Where `ordered` routing places the former main row. */
|
|
4
|
+
export type OrderedPlacement = 'roster' | 'main-first' | 'fallback-first';
|
|
5
|
+
export interface ResolvedRoutingMode {
|
|
6
|
+
mode: RoutingMode;
|
|
7
|
+
placement: OrderedPlacement;
|
|
8
|
+
}
|
|
9
|
+
/** The former main row's id when the caller supplies none. */
|
|
10
|
+
export declare const DEFAULT_FORMER_MAIN_ID = "main";
|
|
11
|
+
/**
|
|
12
|
+
* Resolves a persisted `routing.mode` value. `main-first` and
|
|
13
|
+
* `fallback-first` are aliases of `ordered` that move the former main row;
|
|
14
|
+
* an absent or unrecognised value is `ordered` in roster order. The
|
|
15
|
+
* persisted value is only read here, never rewritten.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveRoutingMode(value: unknown): ResolvedRoutingMode;
|
|
18
|
+
/**
|
|
19
|
+
* Orders `ids` (in roster order) for a placement: `main-first` moves the row
|
|
20
|
+
* named `formerMainId` to the front, `fallback-first` to the back, and
|
|
21
|
+
* `roster` (or a missing former main row) keeps roster order.
|
|
22
|
+
*/
|
|
23
|
+
export declare function orderForPlacement(ids: readonly string[], placement: OrderedPlacement, formerMainId?: string): string[];
|
|
24
|
+
export interface OrderedRouteInput extends AdmissionInput {
|
|
25
|
+
placement?: OrderedPlacement;
|
|
26
|
+
formerMainId?: string;
|
|
27
|
+
/** Killswitch verdict per row; `false` drops the row, a missing row passes. */
|
|
28
|
+
killswitch?: ReadonlyMap<string, boolean>;
|
|
29
|
+
}
|
|
30
|
+
export interface OrderedRoute {
|
|
31
|
+
/** Admitted, non-killed row ids in the order they are to be tried. */
|
|
32
|
+
order: string[];
|
|
33
|
+
admission: AdmissionResult;
|
|
34
|
+
}
|
|
35
|
+
export declare function routeOrdered(input: OrderedRouteInput): OrderedRoute;
|
|
36
|
+
export interface OrderedAttempt {
|
|
37
|
+
id: string;
|
|
38
|
+
/** The response status; undefined when no response arrived. */
|
|
39
|
+
status?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The next row to try, given the attempts so far: the first row when none
|
|
43
|
+
* has been tried, the next untried row when the last attempt's status is one
|
|
44
|
+
* of `retryStatuses`, and undefined otherwise (the last response stands) or
|
|
45
|
+
* when every row has been tried.
|
|
46
|
+
*/
|
|
47
|
+
export declare function nextOrderedAttempt(order: readonly string[], attempts: readonly OrderedAttempt[], retryStatuses: readonly number[]): string | undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface StickyPin {
|
|
2
|
+
accountId: string;
|
|
3
|
+
/** The row's recorded wire identity when the pin was made, if known. */
|
|
4
|
+
wireIdentity?: string;
|
|
5
|
+
/** The request size recorded with the pin; weighs pending bytes. */
|
|
6
|
+
inputBytes?: number;
|
|
7
|
+
/** The projection time the placement was judged on. */
|
|
8
|
+
quotaCheckedAt?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A pin survives while its row id is in `validIds` and the row still holds
|
|
12
|
+
* the account it was placed on. Only two known, differing identities prove
|
|
13
|
+
* the account changed: an unknown identity on either side keeps the pin,
|
|
14
|
+
* because re-placing a session on missing evidence throws its cache away.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isPinValid(pin: StickyPin, validIds: ReadonlySet<string>, currentIdentity: string | undefined): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Sums the request bytes of every other session's pin per row, as openai-auth
|
|
19
|
+
* does. A pin counts only while it was judged on the row's current
|
|
20
|
+
* projection time, so a fresh reading resets the row's pending load.
|
|
21
|
+
*/
|
|
22
|
+
export declare function pendingBytesForPins(pins: Iterable<readonly [sessionKey: string, pin: StickyPin]>, quotaCheckedAtById: ReadonlyMap<string, number | undefined>, excludedSessionKey?: string): Map<string, number>;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { type ProjectedQuota } from '../quota/projection.js';
|
|
2
|
+
import { type AdmissionInput, type AdmissionRefusal, type AdmissionResult, type RoutingRow, type WindowRef } from './admission.js';
|
|
3
|
+
import { type StickyPin } from './pins.js';
|
|
4
|
+
export declare const QUOTA_STALENESS_MS: number;
|
|
5
|
+
export declare const MIN_RESET_HOURS: number;
|
|
6
|
+
export declare const MIN_WEIGHT = 0.000001;
|
|
7
|
+
/** The projection's time, else the caller's cache-entry time. */
|
|
8
|
+
export declare function snapshotCheckedAt(quota: ProjectedQuota | null | undefined, entryCheckedAt?: number): number | undefined;
|
|
9
|
+
export type StickyBreakDecision = {
|
|
10
|
+
action: 'retain';
|
|
11
|
+
reason: 'unknown' | 'stale' | 'healthy' | 'transient';
|
|
12
|
+
} | {
|
|
13
|
+
action: 'migrate';
|
|
14
|
+
reason: 'exhausted' | 'permanent' | 'killswitch';
|
|
15
|
+
/** The exhausted limit, named by its (scope, label), not its position. */
|
|
16
|
+
window?: WindowRef;
|
|
17
|
+
resetsAt?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* How the adapter reads a failed response's HTTP status for the break
|
|
21
|
+
* decision. `permanent` moves the session off its row before any quota is
|
|
22
|
+
* consulted; `transient` and `healthy` keep it unless the quota says the row
|
|
23
|
+
* is spent. A missing status means the request failed without a response.
|
|
24
|
+
*/
|
|
25
|
+
export type StickyStatusClass = 'permanent' | 'transient' | 'healthy';
|
|
26
|
+
export type StickyStatusClassifier = (status: number | undefined) => StickyStatusClass;
|
|
27
|
+
/**
|
|
28
|
+
* The status rule used when the adapter supplies none, carried from
|
|
29
|
+
* openai-auth: 401 and 403 are permanent; no response, 0, a non-finite
|
|
30
|
+
* status, 429 and 5xx are transient; anything else leaves the row healthy.
|
|
31
|
+
* A provider whose 403 can mean an organisation or model policy rather than
|
|
32
|
+
* a dead account supplies its own classifier and can fall back to this one
|
|
33
|
+
* for the statuses it does not single out.
|
|
34
|
+
*/
|
|
35
|
+
export declare function defaultStickyStatusClass(status: number | undefined): StickyStatusClass;
|
|
36
|
+
/** Classifies whether a pinned session should leave its row after a failure. */
|
|
37
|
+
export declare function decideStickyBreak(input: {
|
|
38
|
+
quota: ProjectedQuota | null | undefined;
|
|
39
|
+
quotaCheckedAt?: number;
|
|
40
|
+
status?: number;
|
|
41
|
+
now: number;
|
|
42
|
+
killswitchPasses?: boolean;
|
|
43
|
+
/** Defaults to `defaultStickyStatusClass`. */
|
|
44
|
+
classifyStatus?: StickyStatusClassifier;
|
|
45
|
+
}): StickyBreakDecision;
|
|
46
|
+
export declare function sustainableWindowWeight(window: {
|
|
47
|
+
remainingPercent: number;
|
|
48
|
+
resetsAt?: string;
|
|
49
|
+
}, reservePercent: number, now: number): number;
|
|
50
|
+
export interface StickySelectionCandidate {
|
|
51
|
+
accountId: string;
|
|
52
|
+
quota: ProjectedQuota | null | undefined;
|
|
53
|
+
quotaCheckedAt?: number;
|
|
54
|
+
/** Reserve percent per window label; a missing label reserves nothing. */
|
|
55
|
+
reservePercent: Readonly<Record<string, number>>;
|
|
56
|
+
configuredOrder: number;
|
|
57
|
+
resetCreditsApplicable?: number;
|
|
58
|
+
/** `false` excludes the candidate from weighted and fallback placement. */
|
|
59
|
+
killswitchPasses?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface StickySelectionInput {
|
|
62
|
+
candidates: readonly StickySelectionCandidate[];
|
|
63
|
+
pendingBytes: ReadonlyMap<string, number>;
|
|
64
|
+
requestBytes: number;
|
|
65
|
+
now: number;
|
|
66
|
+
onEmptyWeightedSet?: () => void;
|
|
67
|
+
}
|
|
68
|
+
export interface StickySelection {
|
|
69
|
+
accountId: string;
|
|
70
|
+
quotaCheckedAt?: number;
|
|
71
|
+
source: 'weighted' | 'mode-fallback';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Places a session: the lowest projected pressure among candidates with a
|
|
75
|
+
* fresh positive weight, else (`mode-fallback`) the first candidate in
|
|
76
|
+
* configured order, preferring one with an applicable reset credit.
|
|
77
|
+
*/
|
|
78
|
+
export declare function selectStickyCandidate(input: StickySelectionInput): StickySelection | undefined;
|
|
79
|
+
/** Reserve percent per window label; a missing label reserves nothing. */
|
|
80
|
+
export type ReservePercent = Readonly<Record<string, number>>;
|
|
81
|
+
/**
|
|
82
|
+
* Reserve percentages per row: a map keyed by row id, or a function of the
|
|
83
|
+
* row. A row the map lacks, or for which the function returns undefined,
|
|
84
|
+
* takes the shared `reservePercent`.
|
|
85
|
+
*/
|
|
86
|
+
export type RowReservePercent = ReadonlyMap<string, ReservePercent> | ((row: RoutingRow) => ReservePercent | undefined);
|
|
87
|
+
/**
|
|
88
|
+
* What a valid pin does when its row is not dispatched.
|
|
89
|
+
*
|
|
90
|
+
* `keep`: the pin is retained whatever kept its row from this request.
|
|
91
|
+
*
|
|
92
|
+
* `move-on-confirmed-exhaustion`: the pin moves to the row this request is
|
|
93
|
+
* dispatched to when its own row was refused as confirmed exhausted (a spent
|
|
94
|
+
* window with a future reset, or a spent credit budget) or killed by the
|
|
95
|
+
* killswitch. A refusal for unknown quota (no reading yet, a missing window,
|
|
96
|
+
* an exhausted reading without a usable reset) and an exclusion (rate-limit
|
|
97
|
+
* mark, refresh backoff) keep the pin while this request is served elsewhere.
|
|
98
|
+
* With no admissible row the pin is retained either way.
|
|
99
|
+
*/
|
|
100
|
+
export type RefusedPinPolicy = 'keep' | 'move-on-confirmed-exhaustion';
|
|
101
|
+
export interface StickyRouteInput extends AdmissionInput {
|
|
102
|
+
requestBytes: number;
|
|
103
|
+
/** Bytes already committed per row, for example from other sessions' pins. */
|
|
104
|
+
pendingBytes?: ReadonlyMap<string, number>;
|
|
105
|
+
/** Killswitch verdict per row; a missing row passes. */
|
|
106
|
+
killswitch?: ReadonlyMap<string, boolean>;
|
|
107
|
+
/** Reserve percent per window label, for every row without its own. */
|
|
108
|
+
reservePercent?: ReservePercent;
|
|
109
|
+
/** Per-row reserves, which replace `reservePercent` for the rows they cover. */
|
|
110
|
+
rowReservePercent?: RowReservePercent;
|
|
111
|
+
/** Defaults to `keep`. */
|
|
112
|
+
refusedPinPolicy?: RefusedPinPolicy;
|
|
113
|
+
resetCreditsApplicable?: ReadonlyMap<string, number>;
|
|
114
|
+
/** The session's current pin, if it has one. */
|
|
115
|
+
pin?: StickyPin;
|
|
116
|
+
/** Each row's recorded wire identity; missing means unknown. */
|
|
117
|
+
identities?: ReadonlyMap<string, string | undefined>;
|
|
118
|
+
onEmptyWeightedSet?: () => void;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* What the caller does with the session's pin: keep it, replace it with
|
|
122
|
+
* `pin`, or drop it. Under the default `keep` policy a valid pin is always
|
|
123
|
+
* kept, even when this request was routed elsewhere because its row was
|
|
124
|
+
* refused, excluded or killed; `refusedPinPolicy` can move it instead.
|
|
125
|
+
*/
|
|
126
|
+
export type PinAction = {
|
|
127
|
+
action: 'retain';
|
|
128
|
+
} | {
|
|
129
|
+
action: 'assign';
|
|
130
|
+
pin: StickyPin;
|
|
131
|
+
} | {
|
|
132
|
+
action: 'clear';
|
|
133
|
+
} | {
|
|
134
|
+
action: 'none';
|
|
135
|
+
};
|
|
136
|
+
export type StickyRoute = {
|
|
137
|
+
outcome: 'dispatch';
|
|
138
|
+
accountId: string;
|
|
139
|
+
source: 'pin' | 'weighted' | 'mode-fallback';
|
|
140
|
+
quotaCheckedAt?: number;
|
|
141
|
+
pin: PinAction;
|
|
142
|
+
/** Rows that selection chose and admission then refused, in selection order. */
|
|
143
|
+
refusedSelections: AdmissionRefusal[];
|
|
144
|
+
admission: AdmissionResult;
|
|
145
|
+
} | {
|
|
146
|
+
outcome: 'no-admissible-account';
|
|
147
|
+
pin: PinAction;
|
|
148
|
+
refusedSelections: AdmissionRefusal[];
|
|
149
|
+
admission: AdmissionResult;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Routes one request in `sticky-balanced` mode. A valid pin whose row is
|
|
153
|
+
* admitted, not excluded and not killed is dispatched as is. Otherwise
|
|
154
|
+
* selection runs over the non-excluded rows; each selected row admission
|
|
155
|
+
* refused is removed and selection re-runs, so the loop ends either on an
|
|
156
|
+
* admitted row or with no admissible account.
|
|
157
|
+
*/
|
|
158
|
+
export declare function routeSticky(input: StickyRouteInput): StickyRoute;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SetupHostEnrollment } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Keeps the Pi install refusal message stable while exposing safe process details.
|
|
4
|
+
* A clipped tail can start inside a credential, so discard its first line—or
|
|
5
|
+
* the whole tail if it has no newline—before redacting.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SetupCommandError extends Error {
|
|
8
|
+
readonly step = "pi-install";
|
|
9
|
+
readonly exitCode: number | null;
|
|
10
|
+
readonly signal: string | null;
|
|
11
|
+
readonly stderrTail: string;
|
|
12
|
+
constructor(details: {
|
|
13
|
+
exitCode: number | null;
|
|
14
|
+
signal: string | null;
|
|
15
|
+
stderrTail: string;
|
|
16
|
+
stderrTruncated?: boolean;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/** The line setup prints for one host's outcome, e.g. `Pi: not connected`. */
|
|
20
|
+
export declare function setupEnrollmentLine({ host, outcome, }: SetupHostEnrollment): string;
|
|
21
|
+
/**
|
|
22
|
+
* Remember which hosts a failed setup left connected to the vault, against the
|
|
23
|
+
* thrown value. Only objects can carry a record; a thrown primitive is left as
|
|
24
|
+
* it is, and the printed lines are then the only report.
|
|
25
|
+
*/
|
|
26
|
+
export declare function recordSetupEnrollment(failure: unknown, enrollment: readonly SetupHostEnrollment[]): void;
|
|
27
|
+
/**
|
|
28
|
+
* The per-host vault enrollment outcomes a failed `runSetup` recorded for the
|
|
29
|
+
* value it threw, or `undefined` when setup failed before any host's enrollment
|
|
30
|
+
* began, or the value is not one setup threw (or is not an object).
|
|
31
|
+
*/
|
|
32
|
+
export declare function setupEnrollment(error: unknown): readonly SetupHostEnrollment[] | undefined;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SetupIO, SetupPlugin, SetupResult } from './types.js';
|
|
2
|
+
export declare const AMBIGUOUS_OPENCODE_MESSAGE = "Multiple OpenCode generations found. Choose --opencode=1|2|both before running noninteractive setup.";
|
|
3
|
+
export declare function runSetup(plugin: SetupPlugin, argv?: readonly string[], io?: SetupIO): Promise<SetupResult>;
|