@openlimiter/core 0.2.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,151 @@
1
+ import { snapshotIdentity } from "./merge.js";
2
+ import type { ProviderCode, Snapshot } from "./types.js";
3
+ /**
4
+ * What one collection run achieved, and what the cache does about it.
5
+ *
6
+ * The product's whole honesty problem in one file. A provider answering with a
7
+ * 200 is not a reading, and a provider answering with a well formed body whose
8
+ * meaning has changed is worse than a provider answering with nothing: the
9
+ * first is visibly unknown, the second is confidently wrong. So a run reports
10
+ * what it achieved rather than what it received, and drift is a first class
11
+ * outcome with a first class consequence.
12
+ *
13
+ * That consequence is a SUPPRESSION. Removing the affected rows is not enough
14
+ * on its own, because a cache is shared: the desktop, the command line tool,
15
+ * the adapters and the dashboard all read it, and any one of them could write a
16
+ * stale row back before the others noticed. A suppression is a standing
17
+ * instruction in the document itself, so every reader on the machine reaches
18
+ * the same unknown at the same instant, and a row that predates it can never
19
+ * become visible again.
20
+ *
21
+ * Everything here is pure: no clock, no file system, no network. The caller
22
+ * supplies the instant, so a whole history can be replayed and land on the same
23
+ * answer every time.
24
+ */
25
+ /**
26
+ * Why a collection run produced nothing usable.
27
+ *
28
+ * `drift` is separated from every other reason on purpose, and is the only one
29
+ * that suppresses. The rest are ordinary interruptions: the provider was busy,
30
+ * the machine was offline, a credential lapsed. None of those say anything
31
+ * about whether the reading we already hold is still true, so none of them
32
+ * throws it away; the reading ages out through the ordinary freshness rule
33
+ * instead. Drift is the one that says the previous reading's MEANING is in
34
+ * doubt, and that cannot be waited out.
35
+ */
36
+ export declare const COLLECTION_FAILURE_REASONS: readonly ["drift", "network", "authentication", "rate_limited", "remote_error", "local_io"];
37
+ export type CollectionFailureReason = (typeof COLLECTION_FAILURE_REASONS)[number];
38
+ export type CollectionReport = {
39
+ ok: true;
40
+ provider: ProviderCode;
41
+ accountId?: string;
42
+ observedAt: string;
43
+ snapshots: readonly Snapshot[];
44
+ } | {
45
+ ok: false;
46
+ provider: ProviderCode;
47
+ accountId?: string;
48
+ observedAt: string;
49
+ reason: CollectionFailureReason;
50
+ };
51
+ /**
52
+ * A standing instruction to distrust one identity's cached rows.
53
+ *
54
+ * `reason` is a single literal rather than the full failure vocabulary,
55
+ * deliberately: drift is the only failure that suppresses, and a type that
56
+ * could hold "network" would invite a future caller to suppress on a dropped
57
+ * packet, which would throw away a perfectly good reading every time a train
58
+ * went into a tunnel.
59
+ */
60
+ export interface CacheSuppression {
61
+ provider: ProviderCode;
62
+ accountId?: string;
63
+ reason: "drift";
64
+ suppressedAt: string;
65
+ }
66
+ /**
67
+ * How many suppressions one cache document may carry.
68
+ *
69
+ * The same order of magnitude as the row bound, because there is at most one
70
+ * suppression per identity and identities are what rows are keyed by. A
71
+ * document claiming more than this is not a cache with a lot of drift, it is a
72
+ * document somebody wrote by hand.
73
+ */
74
+ export declare const MAX_CACHE_SUPPRESSIONS = 64;
75
+ /** The cache as this module reasons about it: rows, and what to distrust. */
76
+ export interface CacheState {
77
+ snapshots: readonly Snapshot[];
78
+ suppressions: readonly CacheSuppression[];
79
+ }
80
+ /**
81
+ * The one place provider and account identity is decided.
82
+ *
83
+ * A row, a report and a suppression all have to agree on what "the same thing"
84
+ * means, and the way that goes wrong is three near identical comparisons in
85
+ * three files, one of which forgets that an absent account is not the account
86
+ * called "default". So there is one function, everything calls it, and an
87
+ * absent account keys exactly as it always did.
88
+ */
89
+ export declare function collectionIdentity(provider: ProviderCode, accountId?: string): string;
90
+ /** Whether this snapshot belongs to this provider and account. */
91
+ export declare function snapshotBelongsTo(snapshot: Snapshot, provider: ProviderCode, accountId?: string): boolean;
92
+ /**
93
+ * A suppression document as read back from disk.
94
+ *
95
+ * `ok: false` is not "there were none". It means the suppression data could not
96
+ * be believed, and since a suppression is the only thing standing between a
97
+ * stale row and a surface, a suppression list that cannot be read makes every
98
+ * row in that document unknown. That is the fail closed direction: the cost of
99
+ * being wrong here is showing a number that is no longer true, and the cost of
100
+ * being right is one refresh cycle of honest unknown.
101
+ */
102
+ export type SuppressionReadResult = {
103
+ ok: true;
104
+ suppressions: CacheSuppression[];
105
+ } | {
106
+ ok: false;
107
+ };
108
+ /**
109
+ * Read the suppression list out of a cache document.
110
+ *
111
+ * Absent is the ordinary case and reads as an empty list: a document written
112
+ * before suppressions existed has none, which is true. Anything present and
113
+ * unreadable is refused whole rather than partially salvaged, because a
114
+ * half read suppression list would silently un suppress whichever entries
115
+ * failed to parse, which is exactly the value it exists to prevent.
116
+ */
117
+ export declare function readSuppressions(value: unknown): SuppressionReadResult;
118
+ /**
119
+ * Fold one collection report into the cache.
120
+ *
121
+ * Four rules, one per kind of outcome, and no fifth:
122
+ *
123
+ * 1. A successful report replaces every row for its identity and clears that
124
+ * identity's suppression. A provider that has started making sense again
125
+ * is trusted again, and only a real parse can say that.
126
+ * 2. A drift report removes those rows and records the suppression, replacing
127
+ * any earlier one, so the instant always reflects the latest drift.
128
+ * 3. Every other failure changes nothing at all. The rows we hold stay, and
129
+ * age out through the ordinary freshness rule.
130
+ * 4. A report whose instant cannot be read is not applied. An unreadable
131
+ * instant cannot be compared with a row's observation, so it could neither
132
+ * suppress honestly nor be cleared honestly.
133
+ */
134
+ export declare function applyCollectionReport(state: CacheState, report: CollectionReport): CacheState;
135
+ /**
136
+ * The rows a surface may actually see.
137
+ *
138
+ * A row survives only when nothing suppresses its identity, or when it was
139
+ * observed strictly AFTER the suppression that does. Strictly after, not at or
140
+ * after: a row observed in the same millisecond as the drift that suppressed it
141
+ * is the very row the drift was about.
142
+ *
143
+ * Every read goes through here, which is what makes drift instant everywhere.
144
+ * `buildAdvice` is handed the result of this function and never the raw rows,
145
+ * so a suppressed provider is reported unknown by the statusline, the agent
146
+ * context, the dashboard and the tray in the same tick.
147
+ */
148
+ export declare function visibleSnapshots(state: CacheState): Snapshot[];
149
+ /** Identity, exported for the cache writer that has to key rows the same way. */
150
+ export { snapshotIdentity };
151
+ //# sourceMappingURL=collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,gBAAgB,EAAqB,MAAM,YAAY,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGzD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B,6FAO7B,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AAElF,MAAM,MAAM,gBAAgB,GACxB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;CAChC,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,uBAAuB,CAAC;CACjC,CAAC;AAEN;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,KAAoB,CAAC;AAExD,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC/B,YAAY,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAER;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,YAAY,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAMT;AAWD;;;;;;;;;GASG;AACH,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,gBAAgB,EAAE,CAAA;CAAE,GAC9C;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC;AASlB;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,CA2BtE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,gBAAgB,GACvB,UAAU,CAqCZ;AAeD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,EAAE,CAe9D;AAED,iFAAiF;AACjF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,218 @@
1
+ import { mergeSnapshots, snapshotIdentity, MAX_CACHE_ENTRIES } from "./merge.js";
2
+ import { PROVIDER_CODES, ACCOUNT_ID_PATTERN } from "./types.js";
3
+ /**
4
+ * What one collection run achieved, and what the cache does about it.
5
+ *
6
+ * The product's whole honesty problem in one file. A provider answering with a
7
+ * 200 is not a reading, and a provider answering with a well formed body whose
8
+ * meaning has changed is worse than a provider answering with nothing: the
9
+ * first is visibly unknown, the second is confidently wrong. So a run reports
10
+ * what it achieved rather than what it received, and drift is a first class
11
+ * outcome with a first class consequence.
12
+ *
13
+ * That consequence is a SUPPRESSION. Removing the affected rows is not enough
14
+ * on its own, because a cache is shared: the desktop, the command line tool,
15
+ * the adapters and the dashboard all read it, and any one of them could write a
16
+ * stale row back before the others noticed. A suppression is a standing
17
+ * instruction in the document itself, so every reader on the machine reaches
18
+ * the same unknown at the same instant, and a row that predates it can never
19
+ * become visible again.
20
+ *
21
+ * Everything here is pure: no clock, no file system, no network. The caller
22
+ * supplies the instant, so a whole history can be replayed and land on the same
23
+ * answer every time.
24
+ */
25
+ /**
26
+ * Why a collection run produced nothing usable.
27
+ *
28
+ * `drift` is separated from every other reason on purpose, and is the only one
29
+ * that suppresses. The rest are ordinary interruptions: the provider was busy,
30
+ * the machine was offline, a credential lapsed. None of those say anything
31
+ * about whether the reading we already hold is still true, so none of them
32
+ * throws it away; the reading ages out through the ordinary freshness rule
33
+ * instead. Drift is the one that says the previous reading's MEANING is in
34
+ * doubt, and that cannot be waited out.
35
+ */
36
+ export const COLLECTION_FAILURE_REASONS = [
37
+ "drift",
38
+ "network",
39
+ "authentication",
40
+ "rate_limited",
41
+ "remote_error",
42
+ "local_io"
43
+ ];
44
+ /**
45
+ * How many suppressions one cache document may carry.
46
+ *
47
+ * The same order of magnitude as the row bound, because there is at most one
48
+ * suppression per identity and identities are what rows are keyed by. A
49
+ * document claiming more than this is not a cache with a lot of drift, it is a
50
+ * document somebody wrote by hand.
51
+ */
52
+ export const MAX_CACHE_SUPPRESSIONS = MAX_CACHE_ENTRIES;
53
+ /**
54
+ * The one place provider and account identity is decided.
55
+ *
56
+ * A row, a report and a suppression all have to agree on what "the same thing"
57
+ * means, and the way that goes wrong is three near identical comparisons in
58
+ * three files, one of which forgets that an absent account is not the account
59
+ * called "default". So there is one function, everything calls it, and an
60
+ * absent account keys exactly as it always did.
61
+ */
62
+ export function collectionIdentity(provider, accountId) {
63
+ return accountId === undefined ? provider : provider + " " + accountId;
64
+ }
65
+ /** Whether this snapshot belongs to this provider and account. */
66
+ export function snapshotBelongsTo(snapshot, provider, accountId) {
67
+ return (snapshot.provider === provider &&
68
+ collectionIdentity(provider, snapshot.accountId) ===
69
+ collectionIdentity(provider, accountId));
70
+ }
71
+ function parseInstant(value) {
72
+ const parsed = Date.parse(value);
73
+ return Number.isFinite(parsed) ? parsed : null;
74
+ }
75
+ function isRecord(value) {
76
+ return typeof value === "object" && value !== null && !Array.isArray(value);
77
+ }
78
+ function isProviderCode(value) {
79
+ return (typeof value === "string" &&
80
+ PROVIDER_CODES.includes(value));
81
+ }
82
+ /**
83
+ * Read the suppression list out of a cache document.
84
+ *
85
+ * Absent is the ordinary case and reads as an empty list: a document written
86
+ * before suppressions existed has none, which is true. Anything present and
87
+ * unreadable is refused whole rather than partially salvaged, because a
88
+ * half read suppression list would silently un suppress whichever entries
89
+ * failed to parse, which is exactly the value it exists to prevent.
90
+ */
91
+ export function readSuppressions(value) {
92
+ if (value === undefined || value === null)
93
+ return { ok: true, suppressions: [] };
94
+ if (!Array.isArray(value))
95
+ return { ok: false };
96
+ if (value.length > MAX_CACHE_SUPPRESSIONS)
97
+ return { ok: false };
98
+ const suppressions = [];
99
+ for (const entry of value) {
100
+ if (!isRecord(entry))
101
+ return { ok: false };
102
+ if (!isProviderCode(entry["provider"]))
103
+ return { ok: false };
104
+ if (entry["reason"] !== "drift")
105
+ return { ok: false };
106
+ const suppressedAt = entry["suppressedAt"];
107
+ if (typeof suppressedAt !== "string" || parseInstant(suppressedAt) === null) {
108
+ return { ok: false };
109
+ }
110
+ const accountId = entry["accountId"];
111
+ if (accountId !== undefined) {
112
+ if (typeof accountId !== "string" || !ACCOUNT_ID_PATTERN.test(accountId)) {
113
+ return { ok: false };
114
+ }
115
+ }
116
+ suppressions.push({
117
+ provider: entry["provider"],
118
+ reason: "drift",
119
+ suppressedAt,
120
+ ...(accountId === undefined ? {} : { accountId })
121
+ });
122
+ }
123
+ return { ok: true, suppressions };
124
+ }
125
+ /**
126
+ * Fold one collection report into the cache.
127
+ *
128
+ * Four rules, one per kind of outcome, and no fifth:
129
+ *
130
+ * 1. A successful report replaces every row for its identity and clears that
131
+ * identity's suppression. A provider that has started making sense again
132
+ * is trusted again, and only a real parse can say that.
133
+ * 2. A drift report removes those rows and records the suppression, replacing
134
+ * any earlier one, so the instant always reflects the latest drift.
135
+ * 3. Every other failure changes nothing at all. The rows we hold stay, and
136
+ * age out through the ordinary freshness rule.
137
+ * 4. A report whose instant cannot be read is not applied. An unreadable
138
+ * instant cannot be compared with a row's observation, so it could neither
139
+ * suppress honestly nor be cleared honestly.
140
+ */
141
+ export function applyCollectionReport(state, report) {
142
+ if (parseInstant(report.observedAt) === null)
143
+ return state;
144
+ const belongs = (snapshot) => snapshotBelongsTo(snapshot, report.provider, report.accountId);
145
+ const suppressionMatches = (suppression) => suppression.provider === report.provider &&
146
+ collectionIdentity(report.provider, suppression.accountId) ===
147
+ collectionIdentity(report.provider, report.accountId);
148
+ if (report.ok) {
149
+ /* Rows for this identity are dropped before the merge rather than left for
150
+ it, because a successful run that returns FEWER meters than last time
151
+ must not leave the missing ones behind as fresh looking survivors. */
152
+ const kept = state.snapshots.filter((snapshot) => !belongs(snapshot));
153
+ return {
154
+ snapshots: mergeSnapshots(kept, report.snapshots),
155
+ suppressions: state.suppressions.filter((suppression) => !suppressionMatches(suppression))
156
+ };
157
+ }
158
+ if (report.reason !== "drift")
159
+ return state;
160
+ const suppression = {
161
+ provider: report.provider,
162
+ reason: "drift",
163
+ suppressedAt: report.observedAt,
164
+ ...(report.accountId === undefined ? {} : { accountId: report.accountId })
165
+ };
166
+ const others = state.suppressions.filter((existing) => !suppressionMatches(existing));
167
+ return {
168
+ snapshots: state.snapshots.filter((snapshot) => !belongs(snapshot)),
169
+ /* Bounded by dropping the oldest, so a machine that drifts on every
170
+ provider for a month cannot grow the document without limit. */
171
+ suppressions: boundSuppressions([...others, suppression])
172
+ };
173
+ }
174
+ function suppressedMilliseconds(suppression) {
175
+ return parseInstant(suppression.suppressedAt) ?? 0;
176
+ }
177
+ function boundSuppressions(suppressions) {
178
+ if (suppressions.length <= MAX_CACHE_SUPPRESSIONS)
179
+ return [...suppressions];
180
+ return [...suppressions]
181
+ .sort((left, right) => suppressedMilliseconds(right) - suppressedMilliseconds(left))
182
+ .slice(0, MAX_CACHE_SUPPRESSIONS);
183
+ }
184
+ /**
185
+ * The rows a surface may actually see.
186
+ *
187
+ * A row survives only when nothing suppresses its identity, or when it was
188
+ * observed strictly AFTER the suppression that does. Strictly after, not at or
189
+ * after: a row observed in the same millisecond as the drift that suppressed it
190
+ * is the very row the drift was about.
191
+ *
192
+ * Every read goes through here, which is what makes drift instant everywhere.
193
+ * `buildAdvice` is handed the result of this function and never the raw rows,
194
+ * so a suppressed provider is reported unknown by the statusline, the agent
195
+ * context, the dashboard and the tray in the same tick.
196
+ */
197
+ export function visibleSnapshots(state) {
198
+ if (state.suppressions.length === 0)
199
+ return [...state.snapshots];
200
+ return state.snapshots.filter((snapshot) => {
201
+ const observed = parseInstant(snapshot.observedAt);
202
+ for (const suppression of state.suppressions) {
203
+ if (!snapshotBelongsTo(snapshot, suppression.provider, suppression.accountId)) {
204
+ continue;
205
+ }
206
+ /* A row whose own observation cannot be read cannot be proven newer than
207
+ the suppression, so it is not shown. */
208
+ if (observed === null)
209
+ return false;
210
+ if (observed <= suppressedMilliseconds(suppression))
211
+ return false;
212
+ }
213
+ return true;
214
+ });
215
+ }
216
+ /** Identity, exported for the cache writer that has to key rows the same way. */
217
+ export { snapshotIdentity };
218
+ //# sourceMappingURL=collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.js","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * The connection lifecycle, as one pure function and two human tables.
3
+ *
4
+ * A parser is not a connection. The product's central honesty problem is that
5
+ * an implemented parser has been able to look like a live account, so the state
6
+ * a connection is in has to be a value the whole product agrees on rather than
7
+ * a sentence each surface invents. Everything here is data and arithmetic: no
8
+ * clock, no network, no storage, so a surface can replay a connection's whole
9
+ * history and get the same answer every time.
10
+ */
11
+ export declare const CONNECTION_STATES: readonly ["NOT_CONFIGURED", "DETECTED", "NEEDS_AUTH", "READY_TO_ENABLE", "CONNECTING", "CONNECTED", "DEGRADED", "STALE", "AUTH_EXPIRED", "IMPORT_ONLY", "MANUAL", "UNSUPPORTED", "ERROR"];
12
+ export type ConnectionState = (typeof CONNECTION_STATES)[number];
13
+ /**
14
+ * How many consecutive network failures turn a degraded connection into a
15
+ * broken one. One timeout is a bad moment; three in a row is a fault worth
16
+ * showing a person.
17
+ */
18
+ export declare const NETWORK_FAILURE_ERROR_THRESHOLD = 3;
19
+ export type ConnectionEvent =
20
+ /** A supported local tool was found on this machine. */
21
+ {
22
+ kind: "detected";
23
+ }
24
+ /** Setup established that this connection cannot proceed without a secret. */
25
+ | {
26
+ kind: "credential_required";
27
+ }
28
+ /** A secret was accepted into the operating system credential store. */
29
+ | {
30
+ kind: "credential_stored";
31
+ }
32
+ /** The user asked for this connection to start collecting. */
33
+ | {
34
+ kind: "enable_requested";
35
+ }
36
+ /** A remote read finished. `parsed` says whether we understood the body. */
37
+ | {
38
+ kind: "http_response";
39
+ status: number;
40
+ parsed: boolean;
41
+ }
42
+ /** A local read finished, for readers that never speak HTTP. */
43
+ | {
44
+ kind: "local_read";
45
+ parsed: boolean;
46
+ }
47
+ /** A read never reached the provider. `consecutive` counts failures in a row. */
48
+ | {
49
+ kind: "network_failure";
50
+ consecutive: number;
51
+ }
52
+ /** The newest observation aged past its own expiry. */
53
+ | {
54
+ kind: "expiry_passed";
55
+ }
56
+ /** The user removed this connection. */
57
+ | {
58
+ kind: "disconnected";
59
+ }
60
+ /** The product states this source can only be fed by an explicit import. */
61
+ | {
62
+ kind: "declared_import_only";
63
+ }
64
+ /** The product states this source is a user maintained plan. */
65
+ | {
66
+ kind: "declared_manual";
67
+ }
68
+ /** The product states no safe automatic source exists for this product. */
69
+ | {
70
+ kind: "declared_unsupported";
71
+ };
72
+ /**
73
+ * What the transition function cannot work out from the current state alone.
74
+ *
75
+ * The one authentication question is whether this connection has ever worked,
76
+ * and the current state cannot answer it. CONNECTED then a 404 lands in ERROR,
77
+ * and a 401 arriving there would read as "we never had a credential" when what
78
+ * actually happened is that a credential which used to work stopped, which is a
79
+ * different sentence and a different button. So the fact is carried on the
80
+ * connection record, where it belongs, and passed in. It is one way: nothing
81
+ * ever sets it back to false, because a connection that worked once always did.
82
+ */
83
+ export interface ConnectionContext {
84
+ everConnected: boolean;
85
+ }
86
+ /**
87
+ * Advance a connection by one event.
88
+ *
89
+ * Total and pure: every state, event and context triple has an answer, and an
90
+ * event that makes no sense where it arrived leaves the state exactly as it was
91
+ * rather than inventing a transition. The context is required rather than
92
+ * defaulted, because a caller that forgets it would silently get the wrong
93
+ * authentication answer, and a compiler error is cheaper than that.
94
+ */
95
+ export declare function nextConnectionState(state: ConnectionState, event: ConnectionEvent, context: ConnectionContext): ConnectionState;
96
+ /** One sentence per state, written for a person rather than for a log. */
97
+ export declare const connectionSentence: {
98
+ readonly NOT_CONFIGURED: "Not connected.";
99
+ readonly DETECTED: "Found on this machine but not collecting yet.";
100
+ readonly NEEDS_AUTH: "Waiting for a credential.";
101
+ readonly READY_TO_ENABLE: "Ready to start collecting.";
102
+ readonly CONNECTING: "Connecting.";
103
+ readonly CONNECTED: "Connected and collecting.";
104
+ readonly DEGRADED: "The provider is refusing reads for now.";
105
+ readonly STALE: "The last reading is older than it should be.";
106
+ readonly AUTH_EXPIRED: "The stored credential stopped working.";
107
+ readonly IMPORT_ONLY: "Reads only what you import. Nothing is collected automatically.";
108
+ readonly MANUAL: "Shows the plan you entered yourself.";
109
+ readonly UNSUPPORTED: "No safe automatic source exists for this product yet.";
110
+ readonly ERROR: "This connection failed and needs a look.";
111
+ };
112
+ /** The one thing to do next, in the words of the button that does it. */
113
+ export declare const connectionNextAction: {
114
+ readonly NOT_CONFIGURED: "Connect";
115
+ readonly DETECTED: "Enable local integration";
116
+ readonly NEEDS_AUTH: "Add credential";
117
+ readonly READY_TO_ENABLE: "Enable";
118
+ readonly CONNECTING: "Wait";
119
+ readonly CONNECTED: "Refresh now";
120
+ readonly DEGRADED: "Wait for the next retry";
121
+ readonly STALE: "Refresh now";
122
+ readonly AUTH_EXPIRED: "Reconnect";
123
+ readonly IMPORT_ONLY: "Import a payload";
124
+ readonly MANUAL: "Edit plan";
125
+ readonly UNSUPPORTED: "Add manual plan";
126
+ readonly ERROR: "View diagnostics";
127
+ };
128
+ /**
129
+ * Why a connection is not simply working, in a closed vocabulary.
130
+ *
131
+ * A state says WHAT a connection is. A reason says why it got there, and it is
132
+ * the half a person actually needs: "stale" is a shrug, "stale because the
133
+ * stored credential stopped working" is something they can act on. Closed
134
+ * rather than free text, because a reason is rendered next to a button and a
135
+ * provider must never be able to write either one.
136
+ */
137
+ export declare const CONNECTION_REASONS: readonly ["token_expired", "reading_expired", "tool_not_running", "provider_refusing", "shape_mismatch", "network_unreachable", "no_credential"];
138
+ export type ConnectionReason = (typeof CONNECTION_REASONS)[number];
139
+ /** One sentence per reason, written for a person rather than for a log. */
140
+ export declare const connectionReasonSentence: {
141
+ readonly token_expired: "The credential this connection uses stopped working.";
142
+ readonly reading_expired: "The newest reading is older than the window it describes.";
143
+ readonly tool_not_running: "The local tool has not written a reading yet.";
144
+ readonly provider_refusing: "The provider refused the last read.";
145
+ readonly shape_mismatch: "The answer did not match the shape this build reads.";
146
+ readonly network_unreachable: "The last read never reached the provider.";
147
+ readonly no_credential: "No credential has been stored for this connection.";
148
+ };
149
+ /**
150
+ * The connection as one value a surface can render without deciding anything.
151
+ *
152
+ * State, reason, and the one instruction that belongs to that pair. Every field
153
+ * is written by OpenLimiter and never by a provider, which is what keeps a
154
+ * payload from writing the sentence beside a button.
155
+ */
156
+ export interface ConnectionStatus {
157
+ readonly state: ConnectionState;
158
+ readonly reason: ConnectionReason | null;
159
+ /** What the PERSON does next. Never something this app does on their behalf. */
160
+ readonly instruction: string;
161
+ }
162
+ /**
163
+ * The local tool a connection reads through, when one exists.
164
+ *
165
+ * Carried so an instruction can name it. A connection with no local tool, such
166
+ * as a remote read behind a key the person pasted, passes null and gets an
167
+ * instruction that names no tool rather than an invented one.
168
+ */
169
+ export type ConnectionTool = string | null;
170
+ /**
171
+ * One connection status, from a state and an optional reason.
172
+ *
173
+ * Pure, total, and the only place an instruction is chosen. A reason wins the
174
+ * instruction whenever it has one, because it is the more specific fact; with
175
+ * no reason the state's own next action stands. Nothing here reads a clock, a
176
+ * network or a disk, so a surface and a test see the same answer.
177
+ */
178
+ export declare function connectionStatus(state: ConnectionState, reason?: ConnectionReason | null, tool?: ConnectionTool): ConnectionStatus;
179
+ /** The seven providers on the Connect and See catalogue surface. */
180
+ export declare const CATALOGUE_PROVIDER_IDS: readonly ["claude", "openrouter", "codex", "antigravity", "opencode", "grok", "kimi"];
181
+ export type CatalogueProviderId = (typeof CATALOGUE_PROVIDER_IDS)[number];
182
+ export type CataloguePlatform = "windows" | "macos" | "linux";
183
+ export type CatalogueCapabilityMode = "automatic" | "manual" | "event_driven";
184
+ export type CatalogueCapabilityMaturity = "supported" | "experimental";
185
+ export type CatalogueCapabilityLabel = "Supported" | "Event driven" | "Experimental" | "Manual" | "Manual experimental";
186
+ export type CatalogueAuthMode = "existing_local_cli" | "api_key" | "manual";
187
+ export interface CataloguePlatformCapability {
188
+ mode: CatalogueCapabilityMode;
189
+ maturity: CatalogueCapabilityMaturity;
190
+ label: CatalogueCapabilityLabel;
191
+ }
192
+ export interface ProviderCatalogueEntry {
193
+ providerId: CatalogueProviderId;
194
+ displayName: string;
195
+ connectionState: ConnectionState;
196
+ capabilities: Readonly<Record<CataloguePlatform, CataloguePlatformCapability>>;
197
+ authMode: CatalogueAuthMode;
198
+ action: string;
199
+ }
200
+ export interface GeneratedProviderDocument {
201
+ providers?: unknown;
202
+ }
203
+ /**
204
+ * Join generated provider facts to the closed connection state model.
205
+ *
206
+ * The generated document supplies identity, display name, auth mode and the
207
+ * platform set. Product capability maturity is the frozen overlay above. No
208
+ * site or local application is inspected at runtime.
209
+ */
210
+ export declare function queryProviderCatalogue(generated: GeneratedProviderDocument, states?: Readonly<Partial<Record<CatalogueProviderId, ConnectionState>>>): readonly ProviderCatalogueEntry[];
211
+ export interface PlannedProviderEntry {
212
+ specId: string;
213
+ displayName: string;
214
+ action: "Planned";
215
+ }
216
+ export type CatalogueRow = ({
217
+ availability: "connectable";
218
+ } & ProviderCatalogueEntry) | ({
219
+ availability: "planned";
220
+ } & PlannedProviderEntry);
221
+ /**
222
+ * The connections surface showing connectable providers and planned products.
223
+ *
224
+ * The product catalogue presents every supported or planned provider in document
225
+ * order after the connectable set. Planned rows carry no connection state because
226
+ * no background collector or credential exists for them.
227
+ */
228
+ export declare function queryCatalogueRows(generated: GeneratedProviderDocument, states?: Readonly<Partial<Record<CatalogueProviderId, ConnectionState>>>): readonly CatalogueRow[];
229
+ //# sourceMappingURL=connection-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-state.d.ts","sourceRoot":"","sources":["../src/connection-state.ts"],"names":[],"mappings":"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"}