@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,164 @@
|
|
|
1
|
+
import { type ClaustrumClientOptions as ClaustrumEnrollmentClientOptions, type EnrollmentPollOutcome, type EnrollmentTokenFile, writeEnrollmentTokenFile } from '@cortexkit/claustrum-client';
|
|
2
|
+
/**
|
|
3
|
+
* Codes the vault's closed enrollment-refusal vocabulary marks permanent. The
|
|
4
|
+
* client labels some module error frames transient/retry regardless, so the
|
|
5
|
+
* producer's code takes precedence over the client's action for these.
|
|
6
|
+
*/
|
|
7
|
+
export declare const TERMINAL_ENROLLMENT_CODES: ReadonlySet<string>;
|
|
8
|
+
/** Codes that are always worth another ceremony tick, whatever the action says. */
|
|
9
|
+
export declare const RETRYABLE_ENROLLMENT_CODES: ReadonlySet<string>;
|
|
10
|
+
export type EnrollmentDisposition = 'terminal' | 'retry';
|
|
11
|
+
/**
|
|
12
|
+
* Classify an enrollment transport refusal as `(code, disposition)`. The
|
|
13
|
+
* vault's own code wins: a terminal code is terminal even when the client
|
|
14
|
+
* says retry, queue saturation is retryable even when it says gone, and any
|
|
15
|
+
* other code follows the client's action. Errors that are not producer
|
|
16
|
+
* refusals return undefined and are the caller's to rethrow.
|
|
17
|
+
*/
|
|
18
|
+
export declare function classifyEnrollmentError(error: unknown): {
|
|
19
|
+
code: string;
|
|
20
|
+
disposition: EnrollmentDisposition;
|
|
21
|
+
} | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* The name a plugin proposes for one host, for example `openai-auth-opencode`.
|
|
24
|
+
* Each host enrolls separately so the operator can revoke one without the other.
|
|
25
|
+
*/
|
|
26
|
+
export declare function enrollmentName(plugin: string, host: string): string;
|
|
27
|
+
export interface ClaustrumEnrollmentClient {
|
|
28
|
+
enrollPropose(input: {
|
|
29
|
+
name: string;
|
|
30
|
+
requestSecretHash: string;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
requestId: string;
|
|
33
|
+
}>;
|
|
34
|
+
enrollPoll(input: {
|
|
35
|
+
requestId: string;
|
|
36
|
+
requestSecret: string;
|
|
37
|
+
}): Promise<EnrollmentPollOutcome>;
|
|
38
|
+
}
|
|
39
|
+
export interface ClaustrumEnrollmentConnection extends ClaustrumEnrollmentClient {
|
|
40
|
+
close(): void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Connect the enrollment ceremony. Setup calls this; the request path never
|
|
44
|
+
* does. `connectionFile` is required because this library reads no
|
|
45
|
+
* environment and knows no host paths.
|
|
46
|
+
*/
|
|
47
|
+
export declare function connectClaustrumEnrollmentClient(options: ClaustrumEnrollmentClientOptions & {
|
|
48
|
+
connectionFile: string;
|
|
49
|
+
}): Promise<ClaustrumEnrollmentConnection>;
|
|
50
|
+
export type ClaustrumEnrollmentStatus = {
|
|
51
|
+
state: 'idle';
|
|
52
|
+
} | {
|
|
53
|
+
state: 'pending';
|
|
54
|
+
proposedName: string;
|
|
55
|
+
requestId?: string;
|
|
56
|
+
retryCode?: string;
|
|
57
|
+
} | {
|
|
58
|
+
state: 'approved';
|
|
59
|
+
proposedName: string;
|
|
60
|
+
approvedName?: string;
|
|
61
|
+
tokenGeneration: number;
|
|
62
|
+
} | {
|
|
63
|
+
state: 'denied';
|
|
64
|
+
proposedName: string;
|
|
65
|
+
} | {
|
|
66
|
+
state: 'blocked';
|
|
67
|
+
proposedName: string;
|
|
68
|
+
code: string;
|
|
69
|
+
} | {
|
|
70
|
+
state: 'unavailable';
|
|
71
|
+
proposedName: string;
|
|
72
|
+
code: string;
|
|
73
|
+
} | {
|
|
74
|
+
state: 'busy';
|
|
75
|
+
};
|
|
76
|
+
export interface ClaustrumEnrollmentPaths {
|
|
77
|
+
statePath: string;
|
|
78
|
+
tokenPath: string;
|
|
79
|
+
}
|
|
80
|
+
/** The ceremony state file sits next to the token: `x.json` pairs with `x-state.json`. */
|
|
81
|
+
export declare function getClaustrumEnrollmentPaths(tokenPath: string): ClaustrumEnrollmentPaths;
|
|
82
|
+
/**
|
|
83
|
+
* One host's token and state paths. Every host gets its own pair under the
|
|
84
|
+
* plugin's state directory, so an OpenCode enrollment and a Pi enrollment can
|
|
85
|
+
* be approved and revoked independently. A plugin-resolved override replaces
|
|
86
|
+
* the default token path; a relative override resolves against `cwd`.
|
|
87
|
+
*/
|
|
88
|
+
export declare function hostEnrollmentPaths(input: {
|
|
89
|
+
stateDir: string;
|
|
90
|
+
host: string;
|
|
91
|
+
override?: string;
|
|
92
|
+
cwd?: string;
|
|
93
|
+
}): ClaustrumEnrollmentPaths;
|
|
94
|
+
/** Account-file paths are injectable for isolated setup tests; no environment overrides. */
|
|
95
|
+
export interface EnrollmentAncestorOptions {
|
|
96
|
+
passwdPath?: string;
|
|
97
|
+
groupPath?: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Protect setup writes from directory replacement. On POSIX, group write access
|
|
101
|
+
* is safe only when local records prove the effective user's group is private;
|
|
102
|
+
* sticky ancestors are accepted. Windows uses the shared safe-parent policy.
|
|
103
|
+
*/
|
|
104
|
+
export declare function refuseWritableAncestor(parent: string, options?: EnrollmentAncestorOptions): Promise<void>;
|
|
105
|
+
export declare function readClaustrumEnrollmentStatus(paths: ClaustrumEnrollmentPaths, proposedName: string): Promise<ClaustrumEnrollmentStatus>;
|
|
106
|
+
/**
|
|
107
|
+
* Resolve the host's account mode from its enrollment files. It never changes
|
|
108
|
+
* their contents or the host's mode. On Windows, reading them may create a
|
|
109
|
+
* missing private state folder and tighten an enrollment file's permissions to
|
|
110
|
+
* owner-only, as every private-file read there does.
|
|
111
|
+
*/
|
|
112
|
+
export declare function enrollmentAuthority(paths: ClaustrumEnrollmentPaths, proposedName: string): Promise<'vault' | 'local' | 'unreadable'>;
|
|
113
|
+
/**
|
|
114
|
+
* Read fresh bearer material for one scoped operation; never publish it.
|
|
115
|
+
* Re-reading per operation is what lets an operator reissue a token on disk.
|
|
116
|
+
*/
|
|
117
|
+
export declare function readClaustrumEnrollmentToken(tokenPath: string): Promise<EnrollmentTokenFile>;
|
|
118
|
+
/**
|
|
119
|
+
* The enrollment ceremony for one host. Run it from setup only: it proposes,
|
|
120
|
+
* polls and persists, and every step can wait on an operator. The request
|
|
121
|
+
* path reads the resulting token and never calls into this class.
|
|
122
|
+
*/
|
|
123
|
+
export declare class ClaustrumEnrollmentManager {
|
|
124
|
+
#private;
|
|
125
|
+
constructor(options: {
|
|
126
|
+
client: ClaustrumEnrollmentClient;
|
|
127
|
+
paths: ClaustrumEnrollmentPaths;
|
|
128
|
+
proposedName: string;
|
|
129
|
+
now?: () => number;
|
|
130
|
+
mintSecret?: () => string;
|
|
131
|
+
writeTokenFile?: typeof writeEnrollmentTokenFile;
|
|
132
|
+
ancestorOptions?: EnrollmentAncestorOptions;
|
|
133
|
+
});
|
|
134
|
+
status(): Promise<ClaustrumEnrollmentStatus>;
|
|
135
|
+
resetTerminal(): Promise<ClaustrumEnrollmentResetResult>;
|
|
136
|
+
reconcile(): Promise<ClaustrumEnrollmentStatus>;
|
|
137
|
+
}
|
|
138
|
+
export type ClaustrumEnrollmentResetResult = 'reset' | 'idle' | 'refused-pending' | 'refused-approved' | 'busy';
|
|
139
|
+
/** Reset local terminal metadata without connecting to the credential daemon. */
|
|
140
|
+
export declare function resetClaustrumEnrollmentState(paths: ClaustrumEnrollmentPaths, proposedName: string): Promise<ClaustrumEnrollmentResetResult>;
|
|
141
|
+
export type ClaustrumEnrollmentDisconnectResult = 'disconnected' | 'idle' | 'busy';
|
|
142
|
+
/**
|
|
143
|
+
* Disconnect this host from the vault on disk: remove its enrollment token,
|
|
144
|
+
* then its ceremony state, so `enrollmentAuthority` reports `local` again.
|
|
145
|
+
* This is the disconnect every plugin uses. Removing only the token is not
|
|
146
|
+
* enough: an approved state without a token is still vault mode and refuses,
|
|
147
|
+
* by design. It never contacts the credential daemon, so the enrollment stays
|
|
148
|
+
* approved in the vault until the operator revokes it there; a later Connect
|
|
149
|
+
* starts a new ceremony. A pending request is abandoned the same way.
|
|
150
|
+
*
|
|
151
|
+
* Takes the ceremony lock `reconcile()` and `resetClaustrumEnrollmentState`
|
|
152
|
+
* take, and returns `busy` without deleting anything while another process
|
|
153
|
+
* holds it. State naming another consumer refuses with `wrong-consumer` and
|
|
154
|
+
* both files stay. The token goes first: a crash between the two deletions
|
|
155
|
+
* leaves an approved state without a token, which is still vault mode (it
|
|
156
|
+
* refuses rather than falls back to local accounts), and calling this again
|
|
157
|
+
* finishes the job. Returns `idle` when neither file exists. Nothing else in
|
|
158
|
+
* the directory is touched. The token is removed without being parsed, so a
|
|
159
|
+
* corrupt token does not stand in the way. A malformed state file (bad JSON
|
|
160
|
+
* or wrong shape) is removed too, since it is what makes `enrollmentAuthority`
|
|
161
|
+
* report `unreadable`; a state file that fails the safety checks (symlink,
|
|
162
|
+
* wrong owner, not owner-only) refuses with `unsafe-file` and both files stay.
|
|
163
|
+
*/
|
|
164
|
+
export declare function disconnectClaustrumEnrollment(paths: ClaustrumEnrollmentPaths, proposedName: string): Promise<ClaustrumEnrollmentDisconnectResult>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every refusal the `/claustrum` code raises itself. Refusals sent by the
|
|
3
|
+
* vault keep arriving as the client's own `ClaustrumCredentialError` (a
|
|
4
|
+
* `code`, a `class` and an `action`), so callers can tell the vault's verdict
|
|
5
|
+
* apart from a check made on this side.
|
|
6
|
+
*/
|
|
7
|
+
export type ClaustrumConsumerFailureKind = 'closed' | 'not-enrolled' | 'invalid-token' | 'unavailable' | 'identity-changed' | 'identity-unasserted' | 'insufficient-validity' | 'invalid-material' | 'not-active' | 'route-unavailable' | 'route-declined' | 'no-receipt' | 'unsafe-file' | 'invalid-state' | 'wrong-consumer' | 'roster-busy' | 'host-slot-login' | 'host-slot-placeholder' | 'placeholder-refresh';
|
|
8
|
+
export declare class ClaustrumConsumerError extends Error {
|
|
9
|
+
readonly kind: ClaustrumConsumerFailureKind;
|
|
10
|
+
constructor(kind: ClaustrumConsumerFailureKind, message: string);
|
|
11
|
+
}
|
|
12
|
+
/** The structural logger every class here accepts; `/logger`'s `createLogger` satisfies it. */
|
|
13
|
+
export interface ClaustrumLogger {
|
|
14
|
+
info?(message: string, data?: unknown): void;
|
|
15
|
+
warn(message: string, data?: unknown): void;
|
|
16
|
+
debug(message: string, data?: unknown): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host's own auth slot (OpenCode's stored login for the provider, Pi's
|
|
3
|
+
* equivalent) must hold something, or the host drops the provider. Under
|
|
4
|
+
* custody it holds this placeholder: an OAuth-shaped value that is never a
|
|
5
|
+
* credential. Its empty access token also makes the vault's sealer refuse it,
|
|
6
|
+
* should it ever be offered for import.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CUSTODY_PLACEHOLDER_PREFIX = "claustrum-tombstone:v1:";
|
|
9
|
+
export declare function custodyPlaceholderKey(provider: string): string;
|
|
10
|
+
export declare function custodyPlaceholder(provider: string): {
|
|
11
|
+
type: 'oauth';
|
|
12
|
+
access: '';
|
|
13
|
+
refresh: string;
|
|
14
|
+
expires: 0;
|
|
15
|
+
};
|
|
16
|
+
export declare function isCustodyPlaceholderValue(value: unknown): value is string;
|
|
17
|
+
export declare function isCustodyPlaceholder(auth: unknown, provider: string): boolean;
|
|
18
|
+
/** What the host slot holds: the custody placeholder, a real login, or nothing usable. */
|
|
19
|
+
export type HostSlotContent = 'placeholder' | 'login' | 'empty';
|
|
20
|
+
export declare function classifyHostSlot(auth: unknown, provider: string): HostSlotContent;
|
|
21
|
+
/**
|
|
22
|
+
* Check the host slot against the plugin's mode before serving anything.
|
|
23
|
+
*
|
|
24
|
+
* - Custody mode with a real login in the slot fails closed: someone signed in
|
|
25
|
+
* through the host while the vault owns the accounts, and serving either the
|
|
26
|
+
* login or the vault would silently pick one. The plugin surfaces the error
|
|
27
|
+
* and the user chooses (leave custody, or remove the login).
|
|
28
|
+
* - Local mode with the placeholder in the slot also fails: there is no local
|
|
29
|
+
* credential to serve, and the user has to sign in.
|
|
30
|
+
*
|
|
31
|
+
* Returns the slot content when the combination is consistent.
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertHostSlotMatchesMode(input: {
|
|
34
|
+
mode: 'custody' | 'local';
|
|
35
|
+
auth: unknown;
|
|
36
|
+
provider: string;
|
|
37
|
+
}): HostSlotContent;
|
|
38
|
+
/** Refuse to run a local token refresh with the placeholder as the refresh token. */
|
|
39
|
+
export declare function assertNotCustodyPlaceholder(refreshToken: unknown, provider: string): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ClaustrumClientOptions } from '@cortexkit/claustrum-client';
|
|
2
|
+
import type { ClaustrumScopedClient } from './custody.js';
|
|
3
|
+
export { ClaustrumCredentialError, type ClaustrumReporterSource, type EnrollmentTokenFile, } from '@cortexkit/claustrum-client';
|
|
4
|
+
export { ClaustrumUnreachableError, classifyVaultFailure, VAULT_FAILURE_CLASSES, type VaultFailureClass, } from './availability.js';
|
|
5
|
+
export { ClaustrumConsumer, type ClaustrumConsumerOptions, type ClaustrumWatchState, type SendOptions, } from './consumer.js';
|
|
6
|
+
export { type AccountIdentitySource, type ClaustrumFamily, type ClaustrumRosterWatch, type ClaustrumRosterWatchOptions, type ClaustrumScopedAttempt, type ClaustrumScopedClient, ClaustrumScopedCustody, type ClaustrumScopedIdentity, decideScopedRetryAfter401, type IdentityParser, isScopedCredentialRotation, KEPT_RECEIPT_MARGIN_MS, type KeptReceiptState, type ScopedRetryReason, SERVING_MARGIN_MS, type SkippedVaultReason, type SkippedVaultRecord, type VaultCredential, type VaultCredentialType, type VaultGrantReach, type VaultGrantTuple, type VaultInventory, } from './custody.js';
|
|
7
|
+
export { type ClaustrumEnrollmentClient, type ClaustrumEnrollmentConnection, type ClaustrumEnrollmentDisconnectResult, ClaustrumEnrollmentManager, type ClaustrumEnrollmentPaths, type ClaustrumEnrollmentResetResult, type ClaustrumEnrollmentStatus, classifyEnrollmentError, connectClaustrumEnrollmentClient, disconnectClaustrumEnrollment, type EnrollmentDisposition, enrollmentAuthority, enrollmentName, getClaustrumEnrollmentPaths, hostEnrollmentPaths, RETRYABLE_ENROLLMENT_CODES, readClaustrumEnrollmentStatus, readClaustrumEnrollmentToken, resetClaustrumEnrollmentState, TERMINAL_ENROLLMENT_CODES, } from './enrollment.js';
|
|
8
|
+
export { ClaustrumConsumerError, type ClaustrumConsumerFailureKind, type ClaustrumLogger, } from './errors.js';
|
|
9
|
+
export { assertHostSlotMatchesMode, assertNotCustodyPlaceholder, CUSTODY_PLACEHOLDER_PREFIX, classifyHostSlot, custodyPlaceholder, custodyPlaceholderKey, type HostSlotContent, isCustodyPlaceholder, isCustodyPlaceholderValue, } from './host-slot.js';
|
|
10
|
+
export { acquireInsulaQuotaSource, type InsulaQuotaContext, type InsulaQuotaOptions, type InsulaQuotaSource, type InsulaUsageEntry, type InsulaUsageTransport, type InsulaUsageWatch, type InsulaUsageWindow, type InsulaWatchOptions, } from './insula.js';
|
|
11
|
+
export { acceptAccount, type DeclinedAccount, declineAccount, isDeclined, } from './interlock.js';
|
|
12
|
+
export { type AccountMapper, acceptVaultRoute, DEFAULT_ROUTE_PREFIX, declineVaultRoute, type MutateVaultRosterOptions, mutateVaultRoster, type ProjectionOptions, projectVaultRoster, type QuotaReceipt, readVaultRoster, recordVaultAccountQuota, recordVaultQuota, refreshVaultRoster, resolveVaultPrimary, type VaultPrimary, type VaultPrimaryBinding, type VaultPrimaryUnavailableReason, type VaultRosterFile, type VaultRosterLockContext, type VaultRosterRow, vaultRoutingRows, } from './roster.js';
|
|
13
|
+
export { type ChangeNotice, ChangeNoticeReader, RESUBSCRIBE_BACKOFF_MS, SAFETY_POLL_MS, ScopedRosterWatch, type ScopedRosterWatchOptions, type ScopedWatchEnd, type ScopedWatchHandle, type ScopedWatchNoticeShape, type ScopedWatchSource, WATCH_RETRY_MS, type WatchMode, } from './watch.js';
|
|
14
|
+
/**
|
|
15
|
+
* Connect the client that lists and fetches this consumer's vault credentials
|
|
16
|
+
* on the request path. `connectionFile` is required: this library
|
|
17
|
+
* reads no environment, so the plugin resolves the vault's connection file.
|
|
18
|
+
*/
|
|
19
|
+
export declare function connectClaustrumScopedClient(options: ClaustrumClientOptions & {
|
|
20
|
+
connectionFile: string;
|
|
21
|
+
}): Promise<ClaustrumScopedClient>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { ClaustrumClientOptions } from '@cortexkit/claustrum-client';
|
|
2
|
+
import { type WatchMode } from './watch.js';
|
|
3
|
+
/** Insula's window fields are open: plugins choose which limits they understand. */
|
|
4
|
+
export interface InsulaUsageWindow {
|
|
5
|
+
usedPercent: number;
|
|
6
|
+
rawUsedPercent?: number;
|
|
7
|
+
resetsAt?: string;
|
|
8
|
+
windowMinutes?: number;
|
|
9
|
+
windowKind?: string;
|
|
10
|
+
[field: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface InsulaUsageEntry {
|
|
13
|
+
provider: string;
|
|
14
|
+
account?: string;
|
|
15
|
+
fetchedAt?: string;
|
|
16
|
+
source?: string;
|
|
17
|
+
stale?: unknown;
|
|
18
|
+
errorClass?: unknown;
|
|
19
|
+
usage?: {
|
|
20
|
+
primary?: InsulaUsageWindow;
|
|
21
|
+
secondary?: InsulaUsageWindow;
|
|
22
|
+
tertiary?: InsulaUsageWindow;
|
|
23
|
+
extraRateWindows?: Array<{
|
|
24
|
+
id?: string;
|
|
25
|
+
title?: string;
|
|
26
|
+
window?: InsulaUsageWindow;
|
|
27
|
+
}>;
|
|
28
|
+
[field: string]: unknown;
|
|
29
|
+
};
|
|
30
|
+
[field: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
export interface InsulaQuotaContext {
|
|
33
|
+
/** Milliseconds since the epoch, parsed from the producer's fetchedAt. */
|
|
34
|
+
fetchedAt: number;
|
|
35
|
+
source?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface InsulaQuotaOptions<Q> {
|
|
38
|
+
provider: 'codex' | 'claude' | 'antigravity';
|
|
39
|
+
/**
|
|
40
|
+
* Set every converted window's reading time to context.fetchedAt, not poll
|
|
41
|
+
* time. Return undefined for entries the plugin cannot use. Antigravity can
|
|
42
|
+
* receive its own sidebar reading back from insula: accept it only from the
|
|
43
|
+
* vault or when fetchedAt is newer than the plugin's last reading.
|
|
44
|
+
*/
|
|
45
|
+
normalize: (entry: InsulaUsageEntry, context: InsulaQuotaContext) => Q | undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A held-open `usage.watch` stream. `closed` resolves when insula ends the
|
|
49
|
+
* stream (STREAM_END at shutdown, or a reply that ends it) or after `stop()`,
|
|
50
|
+
* and rejects with insula's refusal (an error with a `code`, such as
|
|
51
|
+
* `unknown_method` from an insula that predates the method, or
|
|
52
|
+
* `usage_watch_limit` when 64 watches are already open) or with the
|
|
53
|
+
* transport failure.
|
|
54
|
+
*/
|
|
55
|
+
export interface InsulaUsageWatch {
|
|
56
|
+
stop(): void;
|
|
57
|
+
readonly closed: Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
/** Optional transport for custom scoped clients; getUsage must not retry refusals. */
|
|
60
|
+
export interface InsulaUsageTransport {
|
|
61
|
+
connectionFile: string;
|
|
62
|
+
getUsage(): Promise<unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* Open a `usage.watch` stream, passing each notice's raw JSON body to
|
|
65
|
+
* `onNotice`. Optional: a transport without it is polled as before.
|
|
66
|
+
*/
|
|
67
|
+
watchUsage?(onNotice: (body: Uint8Array) => void): InsulaUsageWatch;
|
|
68
|
+
}
|
|
69
|
+
/** Constructing this transport opens no connection; getUsage opens one per call. */
|
|
70
|
+
export declare function insulaUsageTransport(options: ClaustrumClientOptions & {
|
|
71
|
+
connectionFile: string;
|
|
72
|
+
}): InsulaUsageTransport;
|
|
73
|
+
export interface InsulaWatchOptions {
|
|
74
|
+
/**
|
|
75
|
+
* The provider this watcher reads. A notice that names only other
|
|
76
|
+
* providers then needs no read; unset means every notice does.
|
|
77
|
+
*/
|
|
78
|
+
provider?: string;
|
|
79
|
+
/**
|
|
80
|
+
* setTimeout and clearTimeout for the waits before re-subscribing or retrying
|
|
81
|
+
* a watch. Defaults to the global timers; tests inject virtual ones.
|
|
82
|
+
*/
|
|
83
|
+
setTimeoutImpl?: typeof setTimeout;
|
|
84
|
+
clearTimeoutImpl?: typeof clearTimeout;
|
|
85
|
+
}
|
|
86
|
+
export declare class InsulaQuotaSource {
|
|
87
|
+
#private;
|
|
88
|
+
private readonly transport;
|
|
89
|
+
private readonly key;
|
|
90
|
+
constructor(transport: InsulaUsageTransport, key: string);
|
|
91
|
+
/**
|
|
92
|
+
* The background poll: at most one usage.get per minute per daemon and
|
|
93
|
+
* enrollment token. While a watch is open it reads nothing, because insula's
|
|
94
|
+
* notices announce every new reading. If insula stops announcing an account,
|
|
95
|
+
* that account's last entry grows older than the freshness limit `current()`
|
|
96
|
+
* applies, `current()` returns nothing for it, and the plugin falls back to
|
|
97
|
+
* its own provider poll for that account.
|
|
98
|
+
*/
|
|
99
|
+
refresh(now: number): Promise<void>;
|
|
100
|
+
/** How this source currently learns about usage changes, for logs. */
|
|
101
|
+
watchMode(): WatchMode;
|
|
102
|
+
/**
|
|
103
|
+
* Hold a `usage.watch` open while any watcher wants it, and re-read
|
|
104
|
+
* usage.get when a notice says something changed. Watchers of one source
|
|
105
|
+
* share one watch, since insula admits only 64 per process. Returns the
|
|
106
|
+
* release; the last release closes the watch.
|
|
107
|
+
*/
|
|
108
|
+
watch(options?: InsulaWatchOptions): () => void;
|
|
109
|
+
current(provider: string, accountId: string, now: number): {
|
|
110
|
+
entry: InsulaUsageEntry;
|
|
111
|
+
context: InsulaQuotaContext;
|
|
112
|
+
} | undefined;
|
|
113
|
+
}
|
|
114
|
+
export declare function acquireInsulaQuotaSource(transport: InsulaUsageTransport, tokenPath: string): {
|
|
115
|
+
source: InsulaQuotaSource;
|
|
116
|
+
release(): void;
|
|
117
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The declined-account interlock: the user's "do not use this vault account",
|
|
3
|
+
* kept on the consumer side because the vault has no notion of it.
|
|
4
|
+
*
|
|
5
|
+
* A decline follows the provider account, not the vault record: an entry made
|
|
6
|
+
* while the account was known matches any credential id or alias that logs
|
|
7
|
+
* into that same account, so relabelling, adding an alias or removing and
|
|
8
|
+
* re-adding the account never lifts it. A credential id that now logs into a
|
|
9
|
+
* different known account does not match, because that account has its own
|
|
10
|
+
* policy. While either side's account is unknown the entry falls back to the
|
|
11
|
+
* credential id, so an adapter that makes no identity claim cannot slip past
|
|
12
|
+
* it. Never keyed on the record version, so a token refresh cannot lift it.
|
|
13
|
+
*/
|
|
14
|
+
export interface DeclinedAccount {
|
|
15
|
+
readonly credentialId: string;
|
|
16
|
+
readonly accountIdentity?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function isDeclined(entries: readonly DeclinedAccount[], credentialId: string, accountIdentity?: string): boolean;
|
|
19
|
+
export declare function declineAccount(entries: readonly DeclinedAccount[], credentialId: string, accountIdentity?: string): DeclinedAccount[];
|
|
20
|
+
/**
|
|
21
|
+
* Lift every entry that declines a row: the row's credential ids (the
|
|
22
|
+
* representative and its aliases) under the row's account. An entry for a
|
|
23
|
+
* different known account is left alone even when it names one of these
|
|
24
|
+
* credential ids, so accepting a replacement never re-enables the account the
|
|
25
|
+
* user declined.
|
|
26
|
+
*/
|
|
27
|
+
export declare function acceptAccount(entries: readonly DeclinedAccount[], credentialIds: readonly string[], accountIdentity?: string): DeclinedAccount[];
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { type QuotaMap, type QuotaObservation } from '../quota/index.js';
|
|
2
|
+
import type { RoutingRow } from '../routing/index.js';
|
|
3
|
+
import type { ClaustrumScopedAttempt, ClaustrumScopedCustody, SkippedVaultRecord, VaultCredential, VaultCredentialType, VaultInventory } from './custody.js';
|
|
4
|
+
import { type DeclinedAccount } from './interlock.js';
|
|
5
|
+
/**
|
|
6
|
+
* One vault account as the pool sees it. It carries no bearer material: the
|
|
7
|
+
* access token is fetched from the vault for every send. Several vault
|
|
8
|
+
* records logged into the same provider account collapse into one row, so
|
|
9
|
+
* duplicate logins never count as extra quota.
|
|
10
|
+
*/
|
|
11
|
+
export interface VaultRosterRow {
|
|
12
|
+
routeId: string;
|
|
13
|
+
credentialId: string;
|
|
14
|
+
credentialType: VaultCredentialType;
|
|
15
|
+
accountIdentity?: string;
|
|
16
|
+
/** Other credential ids logged into the same provider account. */
|
|
17
|
+
aliases?: string[];
|
|
18
|
+
state: string;
|
|
19
|
+
label: string;
|
|
20
|
+
email?: string;
|
|
21
|
+
orgName?: string;
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
addedAt: number;
|
|
24
|
+
quota?: QuotaMap;
|
|
25
|
+
/**
|
|
26
|
+
* The vault listed this record in a form this consumer could not use, so the
|
|
27
|
+
* row is the last good projection kept as it was rather than dropped.
|
|
28
|
+
*/
|
|
29
|
+
stale?: true;
|
|
30
|
+
/**
|
|
31
|
+
* The vault's latest list named no account for `credentialId`, so
|
|
32
|
+
* `accountIdentity` is the last account that credential was known to log
|
|
33
|
+
* into, kept until the vault names one again. It proves neither that the
|
|
34
|
+
* credential still logs into that account nor that it changed accounts.
|
|
35
|
+
*/
|
|
36
|
+
unclaimed?: true;
|
|
37
|
+
}
|
|
38
|
+
export interface VaultRosterFile {
|
|
39
|
+
version: 1;
|
|
40
|
+
view?: string;
|
|
41
|
+
/**
|
|
42
|
+
* False when the vault's reply carried records this consumer could not use.
|
|
43
|
+
* An incomplete reply never removes an account it may still hold: every
|
|
44
|
+
* previous member it does not account for is kept.
|
|
45
|
+
*/
|
|
46
|
+
complete: boolean;
|
|
47
|
+
/** The unusable records of an incomplete reply: a safe id and a fixed reason. */
|
|
48
|
+
rejected?: SkippedVaultRecord[];
|
|
49
|
+
rows: VaultRosterRow[];
|
|
50
|
+
declined: DeclinedAccount[];
|
|
51
|
+
}
|
|
52
|
+
/** How a vault credential is presented in the pool. */
|
|
53
|
+
export type AccountMapper = (credential: VaultCredential) => {
|
|
54
|
+
label?: string;
|
|
55
|
+
};
|
|
56
|
+
export interface ProjectionOptions {
|
|
57
|
+
/** Ids already used by local rows; a vault route never takes one. */
|
|
58
|
+
reservedRouteIds?: ReadonlySet<string>;
|
|
59
|
+
/** Prefix for vault route ids, so they read differently from local ids. */
|
|
60
|
+
routePrefix?: string;
|
|
61
|
+
mapAccount?: AccountMapper;
|
|
62
|
+
now?: number;
|
|
63
|
+
}
|
|
64
|
+
export declare const DEFAULT_ROUTE_PREFIX = "vault:";
|
|
65
|
+
/**
|
|
66
|
+
* Project the vault's list onto the previous roster. Pure: callers serialize
|
|
67
|
+
* discovery and commit. Route ids, quota and the time an account was added
|
|
68
|
+
* survive for the same account; a credential that now logs into a different
|
|
69
|
+
* known account gets a new route id and no inherited quota.
|
|
70
|
+
*
|
|
71
|
+
* A record listed without an identity keeps the last account it was known to
|
|
72
|
+
* log into (the row is marked `unclaimed`). Dropping that binding would let a
|
|
73
|
+
* later different account look like an identity learned for the first time
|
|
74
|
+
* and inherit the first account's route and quota.
|
|
75
|
+
*
|
|
76
|
+
* A reply with unusable records is incomplete: a previous member it does not
|
|
77
|
+
* account for (its record was rejected, or a rejected record has no usable id
|
|
78
|
+
* and could be any of them) is kept, as an alias of its live account or as a
|
|
79
|
+
* `stale` row. Only a complete reply removes an account.
|
|
80
|
+
*/
|
|
81
|
+
export declare function projectVaultRoster(previous: VaultRosterFile | undefined, inventory: VaultInventory, options?: ProjectionOptions): VaultRosterFile;
|
|
82
|
+
/**
|
|
83
|
+
* The rows `/routing` selects among. Only enabled, active accounts route;
|
|
84
|
+
* a declined or cold vault account stays listed for the menu but never
|
|
85
|
+
* reaches admission.
|
|
86
|
+
*/
|
|
87
|
+
export declare function vaultRoutingRows(roster: VaultRosterFile | undefined): RoutingRow[];
|
|
88
|
+
export declare function readVaultRoster(path: string): Promise<VaultRosterFile | undefined>;
|
|
89
|
+
/** The ownership assertion available to a roster mutation callback. */
|
|
90
|
+
export interface VaultRosterLockContext {
|
|
91
|
+
assertOwned: () => Promise<void>;
|
|
92
|
+
}
|
|
93
|
+
export interface MutateVaultRosterOptions {
|
|
94
|
+
/**
|
|
95
|
+
* Runs when the new roster is fully staged on disk, immediately before the
|
|
96
|
+
* rename that publishes it, so a caller can confirm a lease of its own at
|
|
97
|
+
* the moment of publication rather than when `change` returned. If it
|
|
98
|
+
* throws, nothing is published and the staged file is removed. The
|
|
99
|
+
* roster's own write lock is reasserted right after it.
|
|
100
|
+
*/
|
|
101
|
+
beforePublish?: () => Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Read, change and write the roster under its write lock. `change` returns
|
|
105
|
+
* undefined to leave the file as it is. Call `assertOwned()` immediately
|
|
106
|
+
* before any rename performed inside `change`; if it throws, do not rename.
|
|
107
|
+
*/
|
|
108
|
+
export declare function mutateVaultRoster<T>(path: string, change: (current: VaultRosterFile | undefined, context: VaultRosterLockContext) => Promise<{
|
|
109
|
+
next?: VaultRosterFile;
|
|
110
|
+
result: T;
|
|
111
|
+
}> | {
|
|
112
|
+
next?: VaultRosterFile;
|
|
113
|
+
result: T;
|
|
114
|
+
}, options?: MutateVaultRosterOptions): Promise<T>;
|
|
115
|
+
/** Mark a vault account declined; it stays listed but never routes. */
|
|
116
|
+
export declare function declineVaultRoute(path: string, routeId: string): Promise<undefined>;
|
|
117
|
+
/** Lift the user's decline for a vault account and every alias of it. */
|
|
118
|
+
export declare function acceptVaultRoute(path: string, routeId: string): Promise<undefined>;
|
|
119
|
+
/**
|
|
120
|
+
* The receipt fields a quota or profile observation must carry to say which
|
|
121
|
+
* send (credential and account) it came from. Pass the
|
|
122
|
+
* `ClaustrumScopedAttempt` that send was authorized with.
|
|
123
|
+
*/
|
|
124
|
+
export type QuotaReceipt = Pick<ClaustrumScopedAttempt, 'credentialId' | 'accountIdentity' | 'accountIdentitySource' | 'expectedAccountIdentity'>;
|
|
125
|
+
/**
|
|
126
|
+
* Merge a quota observation into a vault row through `/quota`'s merge. The
|
|
127
|
+
* write is fenced on the receipt the reading was taken with: it lands only
|
|
128
|
+
* while the row still holds that receipt's credential (as representative or
|
|
129
|
+
* alias) and account, so a slow read for a replaced account can never land on
|
|
130
|
+
* its successor. Returns whether the observation was kept.
|
|
131
|
+
*/
|
|
132
|
+
export declare function recordVaultQuota(path: string, input: QuotaReceipt & {
|
|
133
|
+
routeId: string;
|
|
134
|
+
observation: QuotaObservation;
|
|
135
|
+
}): Promise<boolean>;
|
|
136
|
+
/**
|
|
137
|
+
* Merge a quota observation that belongs to an account rather than to a
|
|
138
|
+
* served credential, such as a background reading from insula. The write
|
|
139
|
+
* lands only while the row is still bound to that exact known account, so a
|
|
140
|
+
* reading taken before a replacement never reaches the successor. Unlike
|
|
141
|
+
* `recordVaultQuota` it never binds an account: a row without a known account
|
|
142
|
+
* is refused. Returns whether the observation was kept.
|
|
143
|
+
*/
|
|
144
|
+
export declare function recordVaultAccountQuota(path: string, input: {
|
|
145
|
+
routeId: string;
|
|
146
|
+
accountIdentity: string;
|
|
147
|
+
observation: QuotaObservation;
|
|
148
|
+
}): Promise<boolean>;
|
|
149
|
+
/**
|
|
150
|
+
* Discover and commit the roster. The lease covers the list call and the
|
|
151
|
+
* commit, not just the write: an opaque view cannot tell a delayed old reply
|
|
152
|
+
* from a newer one, so a run that lost its lease is refused at commit. A
|
|
153
|
+
* failed list, or custody switching off mid-run, never writes anything; a
|
|
154
|
+
* peer holding the lease means its last committed roster is served instead.
|
|
155
|
+
*/
|
|
156
|
+
export declare function refreshVaultRoster(options: {
|
|
157
|
+
path: string;
|
|
158
|
+
custody: Pick<ClaustrumScopedCustody, 'discover'>;
|
|
159
|
+
isActive?: () => boolean | Promise<boolean>;
|
|
160
|
+
signal?: AbortSignal;
|
|
161
|
+
projection?: ProjectionOptions | (() => ProjectionOptions);
|
|
162
|
+
}): Promise<VaultRosterFile | undefined>;
|
|
163
|
+
/**
|
|
164
|
+
* A host-owned main account as last verified from the vault: the plugin's
|
|
165
|
+
* logical route, the credential that serves it and the account it logs into.
|
|
166
|
+
*/
|
|
167
|
+
export interface VaultPrimaryBinding {
|
|
168
|
+
routeId: string;
|
|
169
|
+
credentialId: string;
|
|
170
|
+
accountIdentity: string;
|
|
171
|
+
view: string;
|
|
172
|
+
}
|
|
173
|
+
export type VaultPrimaryUnavailableReason = 'malformed' | 'unclaimed' | 'incomplete' | 'identity-changed';
|
|
174
|
+
export type VaultPrimary =
|
|
175
|
+
/**
|
|
176
|
+
* Main is bound. `replaced` is the previous binding when it named a
|
|
177
|
+
* different account: everything owned by that account (quota, profile,
|
|
178
|
+
* backoff, cache affinity) must be dropped, and outstanding observations
|
|
179
|
+
* for it fenced, even though the logical route stays the same.
|
|
180
|
+
*/
|
|
181
|
+
{
|
|
182
|
+
status: 'ready';
|
|
183
|
+
binding: VaultPrimaryBinding;
|
|
184
|
+
replaced?: VaultPrimaryBinding;
|
|
185
|
+
}
|
|
186
|
+
/** A complete reply lists no primary record: there is no main. */
|
|
187
|
+
| {
|
|
188
|
+
status: 'absent';
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The reply cannot say who main is. Not the same as absent: the last
|
|
192
|
+
* verified binding is handed back untouched, and serving it still needs a
|
|
193
|
+
* fresh receipt that matches it.
|
|
194
|
+
*/
|
|
195
|
+
| {
|
|
196
|
+
status: 'unavailable';
|
|
197
|
+
reason: VaultPrimaryUnavailableReason;
|
|
198
|
+
lastVerified?: VaultPrimaryBinding;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* The seam for a plugin that keeps a main account: one fixed account the host
|
|
202
|
+
* owns under a logical route, outside the pool's rotating rows. The library
|
|
203
|
+
* has no main convention of its own: the plugin names the
|
|
204
|
+
* vault record that holds the role (`primaryCredentialId`) and its logical
|
|
205
|
+
* route. Main follows the account that record claims; another credential of
|
|
206
|
+
* that same account may serve it when the record itself is not active, but a
|
|
207
|
+
* credential of any other account is never promoted into main.
|
|
208
|
+
*
|
|
209
|
+
* Inputs are the plugin's filtered inventory, whether that inventory is
|
|
210
|
+
* complete (defaults to the reply having no unusable records; pass the
|
|
211
|
+
* roster's `complete` or false to override), the previous verified binding
|
|
212
|
+
* and, on the request path, the account the chosen request expects.
|
|
213
|
+
*/
|
|
214
|
+
export declare function resolveVaultPrimary(input: {
|
|
215
|
+
inventory: VaultInventory;
|
|
216
|
+
complete?: boolean;
|
|
217
|
+
previous?: VaultPrimaryBinding;
|
|
218
|
+
expectedAccountIdentity?: string;
|
|
219
|
+
primaryCredentialId: string;
|
|
220
|
+
routeId: string;
|
|
221
|
+
}): VaultPrimary;
|