@cortexkit/anthropic-auth-core 2.0.0 → 2.0.2

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.
@@ -29,5 +29,5 @@ export declare function acquireRefreshFileLock(options: {
29
29
  onContended?: () => void;
30
30
  /** Synchronous, never awaited; throws and returned promise/thenable rejections are isolated. */
31
31
  onRenewalTimer?: (event: 'scheduled' | 'cancelled') => void;
32
- onStep?: (step: 'stale-marker-stat' | 'stale-marker-claimed' | 'stale-lock-observed' | 'stale-lock-confirmed' | 'stale-lock-removed' | 'stale-lock-recreated' | 'eviction-marker-acquired' | 'renewal-owner-confirmed' | 'renewal-marker-unavailable' | 'renewal-write-fenced' | 'renewal-write-ready' | 'relinquish-read' | 'renewal-finished' | 'release-owner-confirmed') => void | Promise<void>;
32
+ 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>;
33
33
  }): Promise<RefreshFileLock | null>;
@@ -62,11 +62,10 @@ export interface LoggerInstance {
62
62
  /** Operator override of the level floor; undefined removes it. */
63
63
  setLogLevel(level: Level | undefined): void;
64
64
  /**
65
- * Write whatever is buffered. Safe to call synchronously from a
66
- * process-exit handler, which is how each host drains the buffer on
67
- * shutdown.
65
+ * On Windows this returns a promise and must be awaited before shutdown;
66
+ * synchronous process-exit flushing remains available on POSIX only.
68
67
  */
69
- flushLogs(): void;
68
+ flushLogs(): void | Promise<void>;
70
69
  }
71
70
  /**
72
71
  * A logger instance of its own, configured with `options`. A plugin whose
@@ -84,11 +83,10 @@ export declare function createLoggerInstance(options: LoggerOptions): LoggerInst
84
83
  export declare function initLogger(options: LoggerOptions): LoggerInstance;
85
84
  export declare function setLogLevel(l: Level | undefined): void;
86
85
  /**
87
- * Write whatever the default logger has buffered. Safe to call synchronously
88
- * from a process-exit handler, which is how each host drains the buffer on
89
- * shutdown.
86
+ * Write whatever the default logger has buffered. On Windows await the
87
+ * promise before shutdown; POSIX keeps synchronous process-exit flushing.
90
88
  */
91
- export declare function flushLogs(): void;
89
+ export declare function flushLogs(): void | Promise<void>;
92
90
  export declare function createLogger(channel: string): ChannelLogger;
93
91
  export declare function flushForTest(): Promise<void>;
94
92
  /**
@@ -5,6 +5,8 @@ export declare class PiSlotError extends Error {
5
5
  constructor(code: PiSlotErrorCode);
6
6
  }
7
7
  export interface PiSlotOptions {
8
+ /** Windows checks safe ancestry and creates missing parents owner-only, regardless of this option. POSIX keeps its existing directory policy. */
9
+ privateParents?: boolean;
8
10
  authPath: string;
9
11
  provider: string;
10
12
  /**
@@ -1,5 +1,7 @@
1
1
  export interface QuotaCodec {
2
2
  readonly validate: (value: unknown) => boolean;
3
+ /** Normalize persisted data before validation; older codecs may omit this. */
4
+ readonly decode?: (value: unknown) => unknown;
3
5
  readonly merge: (stored: unknown, observation: unknown) => unknown;
4
6
  }
5
7
  /**
@@ -2,8 +2,8 @@ export type { QuotaCodec } from './codec.js';
2
2
  export { quotaCodec } from './codec.js';
3
3
  export type { QuotaTextForm, QuotaTextOptions } from './format.js';
4
4
  export { formatQuota, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, } from './format.js';
5
- export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
6
- export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, emptyQuotaMap, isQuotaMap, } from './map.js';
5
+ export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaCounter, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
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
9
  export type { ExhaustionReset, ProjectedBudget, ProjectedLimit, ProjectedQuota, } from './projection.js';
@@ -44,16 +44,27 @@ export interface CreditBudgetCleared {
44
44
  checkedAt: number;
45
45
  }
46
46
  export type CreditBudgetEntry = CreditBudgetReading | CreditBudgetCleared;
47
+ export interface QuotaCounter {
48
+ value: number;
49
+ checkedAt: number;
50
+ }
47
51
  export interface QuotaMap {
48
52
  limits: QuotaEntry[];
49
53
  /** Absent means no observation has reported on the budget yet. */
50
54
  budget?: CreditBudgetEntry;
55
+ /** Provider-named counts, each with its own observation time. */
56
+ counters?: Record<string, QuotaCounter>;
51
57
  }
52
58
  export declare function emptyQuotaMap(): QuotaMap;
53
59
  /** The time an entry speaks for: its reading, retirement or absence time. */
54
60
  export declare function entryTime(entry: QuotaEntry): number;
55
61
  export declare function limitKey(scope: string, label: string): string;
56
62
  export declare function isCreditBudgetEntry(value: unknown): value is CreditBudgetEntry;
63
+ export declare function isQuotaCounters(value: unknown): value is Record<string, QuotaCounter>;
64
+ /** Keep usable counters without letting one malformed count hide a quota map. */
65
+ export declare function readQuotaCounters(value: unknown): Record<string, QuotaCounter> | undefined;
66
+ /** Decode persisted quota, dropping malformed counters but not limit evidence. */
67
+ export declare function decodeQuotaMap(value: unknown): QuotaMap | undefined;
57
68
  /**
58
69
  * True when `value` is a well-formed quota map. Unrecognised top-level keys
59
70
  * are tolerated (and preserved by merge) so a newer writer's additions
@@ -1,4 +1,4 @@
1
- import { type QuotaMap } from './map.js';
1
+ import { type QuotaCounter, type QuotaMap } from './map.js';
2
2
  export interface ObservedReading {
3
3
  /** Defaults to `all`. */
4
4
  scope?: string;
@@ -32,6 +32,8 @@ export interface QuotaObservation {
32
32
  coverage?: ObservedPair[];
33
33
  /** Absent means the observation says nothing about the budget. */
34
34
  budget?: ObservedBudget;
35
+ /** Each named count uses its own time, independently of limits and budget. */
36
+ counters?: Record<string, QuotaCounter>;
35
37
  }
36
38
  /** Thrown by `mergeQuotaObservation` when either argument is malformed. */
37
39
  export declare class QuotaCodecError extends TypeError {
@@ -1,4 +1,4 @@
1
- import { type QuotaMap } from './map.js';
1
+ import { type QuotaCounter, type QuotaMap } from './map.js';
2
2
  export interface ProjectedLimit {
3
3
  scope: string;
4
4
  label: string;
@@ -30,6 +30,7 @@ export interface ProjectedQuota {
30
30
  checkedAt?: number;
31
31
  /** Present only when the stored budget is a reading, not cleared. */
32
32
  budget?: ProjectedBudget;
33
+ counters?: Record<string, QuotaCounter>;
33
34
  }
34
35
  /**
35
36
  * Resolves `map` for a request in `scope` (`all` or a model family). A family
@@ -66,6 +66,7 @@ export interface OpenPoolStoreOptions {
66
66
  hold?: (point: HoldPoint, rowId: string) => void | Promise<void>;
67
67
  onLockEvent?: LockEnvironment['onLockEvent'];
68
68
  onLockStep?: LockEnvironment['onLockStep'];
69
+ onRenewalTimer?: LockEnvironment['onRenewalTimer'];
69
70
  }
70
71
  export type PoolLoad = {
71
72
  status: 'ready';
@@ -30,6 +30,10 @@ export interface LockEnvironment {
30
30
  name: string;
31
31
  path: string;
32
32
  }, step: LockStep) => void | Promise<void>;
33
+ onRenewalTimer?: (lock: {
34
+ name: string;
35
+ path: string;
36
+ }, event: 'scheduled' | 'cancelled') => void;
33
37
  }
34
38
  export interface HeldLock {
35
39
  readonly name: string;
@@ -49,6 +49,8 @@ export type StoredCredential = (OAuthCredential & {
49
49
  /** Quota codec supplied by `/quota`: the store never interprets the map itself. */
50
50
  export interface QuotaCodec {
51
51
  validate(value: unknown): boolean;
52
+ /** Normalize persisted quota before validation, when the codec supports it. */
53
+ decode?(value: unknown): unknown;
52
54
  merge(stored: unknown | undefined, observation: unknown): unknown;
53
55
  }
54
56
  /**
@@ -42,8 +42,10 @@ export interface NativeAccountSnapshot {
42
42
  mode: 'local' | 'claustrum';
43
43
  settings: Record<string, unknown>;
44
44
  accounts: NativeAccountView[];
45
- /** Vault session-to-account assignments use this separate file. Local mode keeps the assignment file at the destination recorded by migration. */
45
+ /** Vault storage, or the local destination recorded by the authorized migration journal. */
46
46
  routingPath?: string;
47
+ /** Distinct pre-migration routing file that OpenCode local serving may have recreated. */
48
+ routingRecoveryPath?: string;
47
49
  routingSeed?: NativeVaultRoutingSeed;
48
50
  /** AccountStorage-shaped input for pure policy predicates, never a credential or persistence authority. */
49
51
  policyStorage: AccountStorage;
@@ -0,0 +1,6 @@
1
+ /** Treat malformed or absent settings sections as empty, without accepting arrays. */
2
+ export declare function nativeSettingsRecord(value: unknown): Record<string, unknown>;
3
+ /** Patch one section without replacing unrelated native settings or section fields. */
4
+ export declare function mergeNativeSettingsSection(settings: Readonly<Record<string, unknown>>, section: string, patch: Readonly<Record<string, unknown>>): Record<string, unknown>;
5
+ /** When keep-warm runs continuously, remove any previous start/end hour restriction. */
6
+ export declare function nativeCacheKeepAlways(settings: Readonly<Record<string, unknown>>): Record<string, unknown>;
@@ -6,6 +6,7 @@
6
6
  export declare function usagePublicationRefusalIsFatal(error: unknown): boolean;
7
7
  /** Own advisory writes for one host instance and join them before runtime close. */
8
8
  export declare function createNativeUsagePublisher(channel: string): {
9
+ observe: (write: Promise<unknown>) => Promise<void>;
9
10
  publish(write: () => Promise<unknown>): void;
10
11
  drain(): Promise<void>;
11
12
  };
@@ -126,7 +126,7 @@ export declare function readNativeMigrationJournal(paths: NativePoolPaths): Prom
126
126
  */
127
127
  export declare function nativeMigrationAuthorityPhase(journal: NativeMigrationJournal | undefined): 'committed' | 'retired' | undefined;
128
128
  /** An empty or readable store is not proof that the offline authority flip completed. */
129
- export declare function requireNativePoolAuthority(paths: NativePoolPaths): Promise<void>;
129
+ export declare function requireNativePoolAuthority(paths: NativePoolPaths): Promise<NativeMigrationJournal>;
130
130
  interface JournalHooks {
131
131
  /** Outer controller and pool leases remain held through the atomic journal rename. */
132
132
  assertOwned?: () => Promise<void>;
package/dist/routing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AccountStorage, type RoutingMode } from './accounts.ts';
1
+ import type { AccountStorage, RoutingMode } from './accounts.ts';
2
2
  export declare const CLAUDE_ROUTING_COMMAND_NAME = "claude-routing";
3
3
  export declare const ROUTING_MODES: readonly ['main-first', 'fallback-first', 'sticky-balanced'];
4
4
  export declare const DEFAULT_ROUTING_MODE: RoutingMode;
@@ -13,7 +13,6 @@ type RoutingCommandAction = {
13
13
  type: 'usage';
14
14
  };
15
15
  export declare function getRoutingMode(storage: AccountStorage | null): RoutingMode;
16
- export declare function setRoutingMode(mode: RoutingMode, path?: string): Promise<AccountStorage>;
17
16
  export declare function parseRoutingCommandAction(argumentsText: string): RoutingCommandAction;
18
17
  export declare function buildRoutingStatusSummary(input?: {
19
18
  mode?: RoutingMode;
@@ -106,15 +106,19 @@ export declare class StickySessionRouter {
106
106
  now?: () => number;
107
107
  assignmentTtlMs?: number;
108
108
  vaultSeed?: NativeVaultRoutingSeed;
109
+ localRecoveryPath?: string;
109
110
  });
110
111
  private now;
111
112
  private readState;
112
113
  private readStateMtimeNs;
113
114
  private refreshStateIfChanged;
114
115
  private acquireLock;
116
+ private recoverLocalPins;
117
+ private fillLocalPins;
115
118
  private assignmentIsActive;
116
119
  private prune;
117
120
  private writeState;
121
+ private writeResolutionState;
118
122
  /** Import legacy session pins only after vault account discovery produces a complete verified roster. No empty routing file is published while that roster is incomplete. */
119
123
  prepareLegacySeed(knownRoster?: NativeVaultRosterDocument | null): Promise<void>;
120
124
  private logicalAccountId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/anthropic-auth-core",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,10 +30,12 @@
30
30
  "prepublishOnly": "bun run build"
31
31
  },
32
32
  "devDependencies": {
33
- "@cortexkit/common-auth": "0.14.2"
33
+ "@cortexkit/common-auth": "0.17.0"
34
34
  },
35
35
  "dependencies": {
36
- "@cortexkit/claustrum-client": "0.6.2",
36
+ "@cortexkit/claustrum-client": "0.9.0",
37
+ "@cortexkit/private-files": "0.2.1",
38
+ "@cortexkit/subc-client": "^0.16.0",
37
39
  "jsonc-parser": "^3.3.1",
38
40
  "xxhash-wasm": "^1.1.0"
39
41
  }
@@ -1,27 +0,0 @@
1
- import { type AccountStorage, type OAuthAccount } from './accounts.js';
2
- import type { ClaustrumScopedAccount, ClaustrumScopedCustody } from './claustrum-scoped.js';
3
- export declare class ClaustrumRosterBusyError extends Error {
4
- readonly code = "claustrum_roster_busy";
5
- constructor();
6
- }
7
- export interface ClaustrumScopedRoster {
8
- /** The vault's conventional default record; absent when only labeled accounts exist. */
9
- primary?: ClaustrumScopedAccount;
10
- accounts: readonly OAuthAccount[];
11
- storage: AccountStorage;
12
- view: string;
13
- }
14
- /** Pure identity projection; callers must serialize discovery before persisting it. */
15
- export declare function projectClaustrumScopedRoster(storage: AccountStorage, rows: readonly ClaustrumScopedAccount[], view: string, now?: number): ClaustrumScopedRoster;
16
- /**
17
- * The lease covers LIST and commit, not just the file write: opaque view hashes
18
- * cannot tell a delayed old response from a newer one. A lost lease is checked
19
- * while holding the config lock, before any authoritative membership write.
20
- * Neither local mode nor a failed list can publish an empty inventory.
21
- */
22
- export declare function refreshClaustrumScopedRoster(options: {
23
- path: string;
24
- custody: Pick<ClaustrumScopedCustody, 'discover'>;
25
- signal?: AbortSignal;
26
- now?: () => number;
27
- }): Promise<ClaustrumScopedRoster | undefined>;
@@ -1,31 +0,0 @@
1
- import { type ClaustrumClientOptions } from '@cortexkit/claustrum-client';
2
- import { type ClaustrumScopedAttempt, type ClaustrumScopedClient } from './claustrum-scoped.js';
3
- import { type ClaustrumScopedRoster } from './claustrum-scoped-roster.js';
4
- export { ClaustrumCredentialError as ClaustrumScopedCredentialError } from '@cortexkit/claustrum-client';
5
- export declare function connectClaustrumScopedClient(options?: ClaustrumClientOptions): Promise<ClaustrumScopedClient>;
6
- /** Host-owned lifecycle for metadata discovery. Bearer material is never cached. */
7
- export interface ClaustrumScopedRuntimeOptions {
8
- storagePath: string;
9
- tokenPath: string;
10
- connect: () => Promise<ClaustrumScopedClient>;
11
- /** Fired once per discovery change (including the initial snapshot),
12
- * keyed by the inventory view digest. Repeat polls with an identical
13
- * view do not notify. */
14
- onRoster?: (roster: ClaustrumScopedRoster) => void;
15
- onError?: () => void;
16
- setTimeoutImpl?: typeof setTimeout;
17
- clearTimeoutImpl?: typeof clearTimeout;
18
- pollIntervalMs?: number;
19
- }
20
- export declare class ClaustrumScopedRuntime {
21
- #private;
22
- constructor(options: ClaustrumScopedRuntimeOptions);
23
- snapshot(): ClaustrumScopedRoster | undefined;
24
- refresh(): Promise<ClaustrumScopedRoster | undefined>;
25
- start(): void;
26
- authorize(routeId: string, signal?: AbortSignal): Promise<ClaustrumScopedAttempt>;
27
- reportFailure(attempt: ClaustrumScopedAttempt, status: number, source: 'direct' | 'relay_status_field' | 'relay_message_parse'): Promise<void>;
28
- fetchQuota(routeId: string, fetchImpl?: typeof fetch): Promise<import("./accounts.js").OAuthQuotaSnapshot>;
29
- fetchProfile(routeId: string, fetchImpl?: typeof fetch, signal?: AbortSignal): Promise<import("./accounts.js").OAuthAccountProfile>;
30
- close(): void;
31
- }
@@ -1,53 +0,0 @@
1
- import { type ClaustrumClient, type ClaustrumReporterSource, type EnrollmentTokenFile } from '@cortexkit/claustrum-client';
2
- export type ClaustrumScopedClient = Pick<ClaustrumClient, 'listScoped' | 'getScoped' | 'reportAuthFailureScoped' | 'close'>;
3
- export interface ClaustrumScopedIdentity {
4
- readonly credentialId: string;
5
- readonly accountId: string;
6
- }
7
- export interface ClaustrumScopedAccount extends ClaustrumScopedIdentity {
8
- readonly state: string;
9
- readonly email?: string;
10
- readonly orgName?: string;
11
- }
12
- export interface ClaustrumScopedAttempt extends ClaustrumScopedIdentity {
13
- /** Non-enumerable, memory-only bearer. Authorize again for every dispatch/retry. */
14
- readonly accessToken: string;
15
- readonly recordVersion: number;
16
- readonly expiresAtMs: number;
17
- }
18
- /** Only a new version of the same account may replace an in-flight 401. */
19
- export declare function isScopedCredentialRotation(served: ClaustrumScopedAttempt, current: ClaustrumScopedAttempt | undefined): current is ClaustrumScopedAttempt;
20
- /** Send classes that retry once after a scoped 401, for diagnostics. */
21
- export type ScopedRetrySite = 'model' | 'model-relay' | 'cachekeep' | 'prime' | 'quota-profile' | 'pi-model' | 'pi-model-relay' | 'pi-cachekeep';
22
- /** Why a scoped 401 did or did not retry. Never carries credential material. */
23
- export type ScopedRetryReason = 'rotated' | 'reauthorize-failed' | 'credential-changed' | 'account-changed' | 'version-unchanged';
24
- /**
25
- * Decide whether a request that got a 401 should retry with the freshly
26
- * re-authorized receipt, and record the decision. The vault can refresh a
27
- * credential between dispatch and the 401; this line is the consumer-side
28
- * half of that timeline, pairing with the vault's refresh commit instant when
29
- * a stale report has to be explained. Never logs credential material.
30
- */
31
- export declare function decideScopedRetryAfter401(site: ScopedRetrySite, served: ClaustrumScopedAttempt, current: ClaustrumScopedAttempt | undefined): current is ClaustrumScopedAttempt;
32
- /**
33
- * Shared OpenCode/Pi scoped read plane. There is intentionally no credential cache,
34
- * handle fallback, or get single-flight: each outgoing attempt must be authorized
35
- * by the daemon, including attempts made after a successful earlier request.
36
- * Enrollment tokens are re-read per operation so operator reissue works on disk.
37
- */
38
- export declare class ClaustrumScopedCustody {
39
- #private;
40
- constructor(options: {
41
- client: ClaustrumScopedClient;
42
- tokenPath?: string;
43
- readToken?: () => Promise<EnrollmentTokenFile>;
44
- now?: () => number;
45
- });
46
- discover(signal?: AbortSignal): Promise<{
47
- view: string;
48
- accounts: readonly ClaustrumScopedAccount[];
49
- }>;
50
- authorize(identity: ClaustrumScopedIdentity, signal?: AbortSignal): Promise<ClaustrumScopedAttempt>;
51
- reportFailure(attempt: ClaustrumScopedAttempt, status: number, reporterSource: ClaustrumReporterSource): Promise<void>;
52
- close(): void;
53
- }