agent-shim 0.0.0 → 7.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/LICENSE +201 -0
- package/README.md +233 -0
- package/dist/cli.cjs +69549 -0
- package/dist/index.cjs +3620 -0
- package/dist/index.mjs +3545 -0
- package/dist/types/cliError.d.ts +27 -0
- package/dist/types/config/classify.d.ts +49 -0
- package/dist/types/config/load.d.ts +42 -0
- package/dist/types/config/schema.d.ts +746 -0
- package/dist/types/config/store.d.ts +46 -0
- package/dist/types/frontdoor/capability.d.ts +19 -0
- package/dist/types/frontdoor/connect.d.ts +179 -0
- package/dist/types/frontdoor/ensure.d.ts +34 -0
- package/dist/types/frontdoor/probe.d.ts +13 -0
- package/dist/types/frontdoor/state.d.ts +50 -0
- package/dist/types/frontdoor/supervisor.d.ts +48 -0
- package/dist/types/frontdoor/x509.d.ts +11 -0
- package/dist/types/headroom/ensure.d.ts +31 -0
- package/dist/types/headroom/settings.d.ts +13 -0
- package/dist/types/headroom/source.d.ts +8 -0
- package/dist/types/headroom/state.d.ts +77 -0
- package/dist/types/headroom/supervisor.d.ts +107 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/launcher/farm.d.ts +165 -0
- package/dist/types/launcher/guard.d.ts +57 -0
- package/dist/types/launcher/lock.d.ts +41 -0
- package/dist/types/launcher/ports.d.ts +135 -0
- package/dist/types/legacy.d.ts +24 -0
- package/dist/types/paths.d.ts +79 -0
- package/dist/types/resolve/conditions.d.ts +37 -0
- package/dist/types/resolve/decide.d.ts +45 -0
- package/dist/types/resolve/extends.d.ts +41 -0
- package/dist/types/resolve/flatten.d.ts +13 -0
- package/dist/types/resolve/match.d.ts +58 -0
- package/dist/types/resolve/pipeline.d.ts +30 -0
- package/dist/types/resolve/plan.d.ts +57 -0
- package/dist/types/resolve/projects.d.ts +61 -0
- package/dist/types/resolve/reconcile.d.ts +80 -0
- package/dist/types/resolve/types.d.ts +159 -0
- package/dist/types/resolve/walk.d.ts +85 -0
- package/dist/types/usage/account.d.ts +20 -0
- package/dist/types/usage/read.d.ts +70 -0
- package/dist/types/usage/schema.d.ts +318 -0
- package/package.json +91 -2
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { CategoryMap, CategoryName, Entries, LaunchFlags, OverridableCategory, WhenCondition } from "../config/schema";
|
|
2
|
+
/** A layer's index in the assembled cascade. Strictly ascending in composition order: a higher id was composed later and therefore wins on the comparator's first rank. */
|
|
3
|
+
export type LayerId = number;
|
|
4
|
+
/** Where a cascade layer came from. Purely descriptive — precedence is carried by `LayerId`, never by kind. */
|
|
5
|
+
type LayerKind = "global-config" | "config-profile" | "directory-rule" | "portable" | "portable-local" | "cli-override";
|
|
6
|
+
/** One composable layer of the cascade: its own category toggles and entries overrides, plus the entries key order captured at load time. */
|
|
7
|
+
export interface Layer {
|
|
8
|
+
readonly id: LayerId;
|
|
9
|
+
readonly kind: LayerKind;
|
|
10
|
+
/** Human-readable origin (a file path, or a profile name) used by `agent-shim check` to explain a decision. */
|
|
11
|
+
readonly source: string;
|
|
12
|
+
readonly categories?: CategoryMap;
|
|
13
|
+
readonly entries?: Entries;
|
|
14
|
+
/** The entries object's own key insertion order, from `src/config/load.ts`. Falls back to `Object.keys(entries)` when absent. */
|
|
15
|
+
readonly entryOrder?: readonly string[];
|
|
16
|
+
readonly launch?: LaunchFlags;
|
|
17
|
+
}
|
|
18
|
+
/** Facts about one entry under `~/.claude`, relative to `~/.claude` itself. */
|
|
19
|
+
export interface EntryFact {
|
|
20
|
+
/** Path relative to `~/.claude`, forward-slash separated, never leading or trailing slash. */
|
|
21
|
+
readonly relPath: string;
|
|
22
|
+
readonly isDirectory: boolean;
|
|
23
|
+
/** True when the entry is itself a symlink (including the relative, tree-escaping symlinks that already exist inside a real `~/.claude`). */
|
|
24
|
+
readonly isSymlink: boolean;
|
|
25
|
+
/** The entry's own mtime. */
|
|
26
|
+
readonly mtimeMs: number;
|
|
27
|
+
/** The most recent mtime anywhere in this entry's subtree — its own mtime for a plain file. A directory-scoped `newerThan` must read this, never the directory's own inode mtime, which does not change when a file three levels down is rewritten. */
|
|
28
|
+
readonly latestMtimeMs: number;
|
|
29
|
+
/** The entry's own size. */
|
|
30
|
+
readonly sizeBytes: number;
|
|
31
|
+
/** The recursive total size of this entry's subtree — its own size for a plain file. A directory-scoped `maxSizeBytes` must read this, never the directory's own ~4KB inode size. */
|
|
32
|
+
readonly totalSizeBytes: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Every fact the resolver needs, injected rather than read. Nothing in `src/resolve/` ever touches a real filesystem, git repository, clock, or environment: something else builds this structure and hands it over, which is what makes the whole resolver unit-testable with fake mtimes, a fake branch, and a fake environment.
|
|
36
|
+
*/
|
|
37
|
+
export interface EntryFacts {
|
|
38
|
+
readonly nowMs: number;
|
|
39
|
+
/** The real home directory, used to expand `~` in `history/projects/` patterns. */
|
|
40
|
+
readonly home: string;
|
|
41
|
+
/** The canonical `~/.claude` directory every relative path in `entries` is relative to. */
|
|
42
|
+
readonly claudeHome: string;
|
|
43
|
+
/** The working directory the cascade was resolved for. */
|
|
44
|
+
readonly cwd: string;
|
|
45
|
+
/** The git branch checked out at `cwd`, or undefined when `cwd` is not in a repository. */
|
|
46
|
+
readonly branch?: string;
|
|
47
|
+
/** True when `cwd`'s repository is in detached-HEAD state, in which case no `branch` condition can match. */
|
|
48
|
+
readonly branchDetached?: boolean;
|
|
49
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
50
|
+
/** Every entry under `~/.claude`, keyed by its path relative to `~/.claude`. Directories appear alongside their descendants. */
|
|
51
|
+
readonly entries: ReadonlyMap<string, EntryFact>;
|
|
52
|
+
}
|
|
53
|
+
/** A single entries override, compiled into the form the resolver actually matches against. */
|
|
54
|
+
export interface CompiledRule {
|
|
55
|
+
/** The key exactly as written in its source file, for diagnostics. */
|
|
56
|
+
readonly rawKey: string;
|
|
57
|
+
/** The category prefix the key declared. Cross-checked against the real classification of every path it matches. */
|
|
58
|
+
readonly declaredCategory: CategoryName;
|
|
59
|
+
/** The key with its category prefix stripped and (under `history/projects/`) its path fragment encoded — a pattern over paths relative to `~/.claude`. Two differently-spelled but equivalent keys share one canonical pattern and therefore collapse to one rule. */
|
|
60
|
+
readonly canonicalPattern: string;
|
|
61
|
+
readonly value: boolean;
|
|
62
|
+
readonly when?: WhenCondition;
|
|
63
|
+
readonly layer: LayerId;
|
|
64
|
+
/** The key's position within its own layer's entries object, used only by the comparator's final same-layer tie-break. */
|
|
65
|
+
readonly ordinal: number;
|
|
66
|
+
/** True when the canonical pattern contains no glob metacharacter. */
|
|
67
|
+
readonly isExact: boolean;
|
|
68
|
+
/** The pattern's leading run of literal (non-wildcard) characters. */
|
|
69
|
+
readonly literalPrefix: string;
|
|
70
|
+
/** How many `/`-separated segments the canonical pattern has. */
|
|
71
|
+
readonly segmentCount: number;
|
|
72
|
+
/** Matches the pattern itself and everything beneath it, so a rule on `skills` covers `skills/commit/SKILL.md`. */
|
|
73
|
+
readonly matches: (relPath: string) => boolean;
|
|
74
|
+
}
|
|
75
|
+
/** How a decision was reached, for `agent-shim check`'s "which layer decided this" output. */
|
|
76
|
+
type DecisionVia =
|
|
77
|
+
/** The unconditional pre-cascade floor: the path's real classification is `secret`. */
|
|
78
|
+
"secret-floor"
|
|
79
|
+
/** Nothing in the classification map recognises the path's top-level entry. */
|
|
80
|
+
| "unclassified"
|
|
81
|
+
/** An entries rule matched and its condition (if any) held. */
|
|
82
|
+
| "entry-rule"
|
|
83
|
+
/** No entries rule survived; a layer's category toggle decided it. */
|
|
84
|
+
| "category-override"
|
|
85
|
+
/** No entries rule and no category toggle; the shipped default for the category decided it. */
|
|
86
|
+
| "category-default";
|
|
87
|
+
/** One entry's resolved sharing decision plus the reasoning behind it. */
|
|
88
|
+
export interface Decision {
|
|
89
|
+
readonly relPath: string;
|
|
90
|
+
readonly shared: boolean;
|
|
91
|
+
readonly via: DecisionVia;
|
|
92
|
+
readonly category: CategoryName | null;
|
|
93
|
+
/** The winning entries rule, when `via` is "entry-rule". */
|
|
94
|
+
readonly rule?: CompiledRule;
|
|
95
|
+
/** Rules that matched but whose `when` condition failed, in descending specificity — each was eliminated and resolution continued to the next candidate. */
|
|
96
|
+
readonly eliminated?: readonly EliminatedRule[];
|
|
97
|
+
}
|
|
98
|
+
/** A candidate rule that matched the path but was eliminated because its `when` condition did not hold. */
|
|
99
|
+
export interface EliminatedRule {
|
|
100
|
+
readonly rule: CompiledRule;
|
|
101
|
+
readonly failed: readonly string[];
|
|
102
|
+
}
|
|
103
|
+
/** Every diagnostic the resolver can raise. Codes are stable strings so `agent-shim check` and tests can assert on them. */
|
|
104
|
+
type DiagnosticCode =
|
|
105
|
+
/** A profile's `extends` graph contains a cycle. */
|
|
106
|
+
"EXTENDS_CYCLE"
|
|
107
|
+
/** An `extends` entry names a profile that does not exist. */
|
|
108
|
+
| "MISSING_PROFILE"
|
|
109
|
+
/** An entries key was written under the `secret/` prefix. Rejected at compile time — `secret` can never be overridden by any layer. */
|
|
110
|
+
| "SECRET_ENTRY_KEY"
|
|
111
|
+
/** An entries key under some other prefix matched a path whose real classification is `secret`. Neutralised at resolve time by the floor check. */
|
|
112
|
+
| "SECRET_PATH_NEUTRALISED"
|
|
113
|
+
/** An entries key's declared category prefix disagrees with the real classification of a path it matched. */
|
|
114
|
+
| "CATEGORY_PREFIX_MISMATCH"
|
|
115
|
+
/** An entries key is not of the form `<category>/<path>`. The schema rejects these at parse time; this fires only for a layer built in code. */
|
|
116
|
+
| "MALFORMED_ENTRY_KEY"
|
|
117
|
+
/** An earlier layer's exact key lost to a later layer's glob. Correct per the comparator, but worth surfacing rather than resolving silently. */
|
|
118
|
+
| "EXACT_ENTRY_OVERRIDDEN_BY_LATER_GLOB"
|
|
119
|
+
/** A `history/projects/` pattern's encoded form could plausibly correspond to more than one real path. */
|
|
120
|
+
| "AMBIGUOUS_PROJECT_ENCODING"
|
|
121
|
+
/** A `history/projects/` key's path fragment is neither home-rooted nor absolute, so it cannot be encoded. */
|
|
122
|
+
| "UNROOTED_PROJECT_PATH"
|
|
123
|
+
/** A `~/.claude` entry nothing in the classification map recognises. */
|
|
124
|
+
| "UNCLASSIFIED_ENTRY"
|
|
125
|
+
/** An empty `when: {}` object, which is vacuously true and therefore has no effect. */
|
|
126
|
+
| "EMPTY_WHEN"
|
|
127
|
+
/** The previous farm's manifest is missing or unreadable, so reconciliation ran in conservative mode. */
|
|
128
|
+
| "FARM_MANIFEST_MISSING"
|
|
129
|
+
/** A file Claude Code wrote into a materialised farm directory differs from the canonical copy of the same path. */
|
|
130
|
+
| "RECONCILE_CONFLICT"
|
|
131
|
+
/** Reconciliation proposed adopting a path whose real classification is `secret`. Refused: the secret floor applies to data moving into `~/.claude` exactly as it applies to data moving out. */
|
|
132
|
+
| "RECONCILE_SECRET_BLOCKED"
|
|
133
|
+
/** A previous launch was interrupted mid-swap and this launch completed or undid the half-finished work. */
|
|
134
|
+
| "FARM_SWAP_RECOVERED"
|
|
135
|
+
/** A superseded farm could not be discarded because it still held data the new farm has its own entry for, so it was left on disk for the user to look at. */
|
|
136
|
+
| "FARM_PREVIOUS_RETAINED";
|
|
137
|
+
/** Severity of a diagnostic. An `error` means a configuration layer asked for something the tool refused to do. */
|
|
138
|
+
type DiagnosticSeverity = "error" | "warning" | "info";
|
|
139
|
+
/** One structured diagnostic. */
|
|
140
|
+
export interface Diagnostic {
|
|
141
|
+
readonly code: DiagnosticCode;
|
|
142
|
+
readonly severity: DiagnosticSeverity;
|
|
143
|
+
readonly message: string;
|
|
144
|
+
/** The config key, profile name, or entry path the diagnostic concerns. */
|
|
145
|
+
readonly subject?: string;
|
|
146
|
+
/** The layer that raised it, when the diagnostic is attributable to one. */
|
|
147
|
+
readonly layer?: LayerId;
|
|
148
|
+
}
|
|
149
|
+
/** The flattened result of cascade phase one. */
|
|
150
|
+
export interface FlattenedCascade {
|
|
151
|
+
/** The last value each overridable category was set to across every layer. Categories no layer touched are absent. */
|
|
152
|
+
readonly categories: ReadonlyMap<OverridableCategory, boolean>;
|
|
153
|
+
/** One compiled rule per canonical pattern — later layers having already overwritten earlier ones for identical patterns. */
|
|
154
|
+
readonly rules: ReadonlyMap<string, CompiledRule>;
|
|
155
|
+
/** Launch flags, last layer wins per field. */
|
|
156
|
+
readonly launch: LaunchFlags;
|
|
157
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
158
|
+
}
|
|
159
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { DirectoryRule, GlobalConfig, PortableConfig } from "../config/schema";
|
|
2
|
+
import { type ProfileLoader } from "./extends";
|
|
3
|
+
import type { Diagnostic, Layer } from "./types";
|
|
4
|
+
/** Injected predicate answering whether a directory can be read, so the walk stays pure. */
|
|
5
|
+
export type ReadablePredicate = (dir: string) => boolean;
|
|
6
|
+
/** Options for the upward directory walk. */
|
|
7
|
+
export interface WalkOptions {
|
|
8
|
+
/** The user's home directory, the walk's default stopping point. */
|
|
9
|
+
readonly home: string;
|
|
10
|
+
/** Where to stop walking upward, inclusive. Defaults to `home`, and is what `walkUpLimit` in the global config sets. */
|
|
11
|
+
readonly limit?: string;
|
|
12
|
+
/** Answers whether a directory is readable. A directory that is not stops the walk there rather than failing the launch. */
|
|
13
|
+
readonly isReadable?: ReadablePredicate;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Collects `cwd` and every ancestor of it, shallowest-first, the way Claude Code itself resolves nested `CLAUDE.md` files.
|
|
17
|
+
*
|
|
18
|
+
* The walk stops at (and includes) the limit directory — `$HOME` by default — or at the filesystem root when `cwd` is not beneath the limit at all. An ancestor that cannot be read stops the walk there rather than failing the launch: a directory you cannot read cannot carry a config file you were meant to obey.
|
|
19
|
+
*
|
|
20
|
+
* `cwd` is expected to already be a real path; resolving symlinks is the caller's job, so this stays free of filesystem access.
|
|
21
|
+
*/
|
|
22
|
+
export declare function walkDirectoryAncestors(cwd: string, options: WalkOptions): string[];
|
|
23
|
+
/** The up-to-three configuration sources that can apply at one directory level. */
|
|
24
|
+
export interface DirectoryLevelSources {
|
|
25
|
+
readonly dir: string;
|
|
26
|
+
/** The committed, team-shared `.agent-shim.json` at this level. */
|
|
27
|
+
readonly portable?: {
|
|
28
|
+
readonly config: PortableConfig;
|
|
29
|
+
readonly entryOrder?: readonly string[];
|
|
30
|
+
readonly filepath: string;
|
|
31
|
+
};
|
|
32
|
+
/** Every rule in this user's own `~/.agent-shim/directory-rules.json` whose path is exactly this level, in file order. */
|
|
33
|
+
readonly rules?: readonly {
|
|
34
|
+
readonly rule: DirectoryRule;
|
|
35
|
+
readonly entryOrder?: readonly string[];
|
|
36
|
+
readonly filepath: string;
|
|
37
|
+
}[];
|
|
38
|
+
/** The gitignored `.agent-shim.local.json` at this level. */
|
|
39
|
+
readonly portableLocal?: {
|
|
40
|
+
readonly config: PortableConfig;
|
|
41
|
+
readonly entryOrder?: readonly string[];
|
|
42
|
+
readonly filepath: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Everything needed to assemble the full ordered layer sequence, all of it already loaded by the caller. */
|
|
46
|
+
export interface CascadeInput {
|
|
47
|
+
readonly home: string;
|
|
48
|
+
/** The user-global `~/.agent-shim/config.json`, the least-specific layer above the shipped defaults. */
|
|
49
|
+
readonly globalConfig?: {
|
|
50
|
+
readonly config: GlobalConfig;
|
|
51
|
+
readonly entryOrder?: readonly string[];
|
|
52
|
+
readonly filepath: string;
|
|
53
|
+
};
|
|
54
|
+
/** The configuration profile chosen by the caller's own precedence rules: explicit flag, then the active identity's default, then the global default. */
|
|
55
|
+
readonly baseConfigProfile?: string;
|
|
56
|
+
readonly loadProfile: ProfileLoader;
|
|
57
|
+
/** Directory levels, shallowest-first, as produced by `walkDirectoryAncestors`. */
|
|
58
|
+
readonly levels?: readonly DirectoryLevelSources[];
|
|
59
|
+
/** One-off overrides from the command line or environment, composed last so they beat everything. */
|
|
60
|
+
readonly cliOverride?: {
|
|
61
|
+
readonly categories?: Layer["categories"];
|
|
62
|
+
readonly entries?: Layer["entries"];
|
|
63
|
+
readonly entryOrder?: readonly string[];
|
|
64
|
+
readonly launch?: Layer["launch"];
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** The assembled cascade: an ordered layer sequence plus what the walk itself decided. */
|
|
68
|
+
export interface AssembledCascade {
|
|
69
|
+
readonly layers: readonly Layer[];
|
|
70
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
71
|
+
/** The deepest directory-level `identity` pin encountered, which the launcher uses as a safety net beneath an explicit `@name`. */
|
|
72
|
+
readonly identityPin?: string;
|
|
73
|
+
/** The deepest directory-level `configProfile` selection encountered, which is what `agent-shim configure` treats as the active profile for a path. */
|
|
74
|
+
readonly directoryConfigProfile?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Assembles the full ordered layer sequence.
|
|
78
|
+
*
|
|
79
|
+
* Order is: the user-global config, then the base configuration profile's own `extends` chain, then every directory level shallowest-to-deepest, then one-off command-line overrides.
|
|
80
|
+
*
|
|
81
|
+
* Within one directory level, up to three sources fold most-personal-last: the committed `.agent-shim.json` (team-shared), then this user's own `directory-rules.json` entries for that exact path, then `.agent-shim.local.json` (this clone, this user, never committed). That three-source fold happens once per level, and the whole shallowest-to-deepest walk is one continuous sequence through those folded levels — a deeper level's three-source result composes on top of a shallower level's, never gathered per-source across the whole tree first.
|
|
82
|
+
*
|
|
83
|
+
* A level source that names a `configProfile` composes that profile's whole chain in *before* its own inline overrides, so the source reads as "everything that profile says, plus what is additionally true this far down the tree" rather than swapping the base profile out wholesale.
|
|
84
|
+
*/
|
|
85
|
+
export declare function assembleCascade(input: CascadeInput): AssembledCascade;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CliError } from "../cliError";
|
|
2
|
+
import type { FarmFs } from "../launcher/ports";
|
|
3
|
+
import { type AccountMetadata } from "./schema";
|
|
4
|
+
/** Raised when an identity's `.claude.json` exists but cannot be read as account metadata (not a JSON object, or an `oauthAccount` field of an unexpected type): reported, never read as "no account". */
|
|
5
|
+
export declare class AccountMetadataError extends CliError {
|
|
6
|
+
readonly file: string;
|
|
7
|
+
constructor(file: string, detail: string);
|
|
8
|
+
}
|
|
9
|
+
/** Whether `name` is a valid identity name, which is also what makes it safe as one path component under the identities and snapshots directories (no separator, no leading dot). */
|
|
10
|
+
export declare function isIdentityName(name: string): boolean;
|
|
11
|
+
/** The path of an identity's `.claude.json`. */
|
|
12
|
+
export declare function claudeJsonPath(identitiesDir: string, identity: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Reads an identity's account metadata from the `oauthAccount` block Claude Code writes into the identity's `.claude.json` when it logs in: the fields `AccountMetadataSchema` names and nothing else (the token is never in this file; Claude Code keeps it in the Keychain or `.credentials.json`). Undefined when the identity has no `.claude.json` or no `oauthAccount` (a setup-token identity, or one never logged in). Throws `AccountMetadataError` for a file that is not a JSON object, and on an invalid identity name, which could otherwise name a path outside the identities directory.
|
|
15
|
+
*/
|
|
16
|
+
export declare function readAccountMetadata(fs: Pick<FarmFs, "readFileUtf8">, identitiesDir: string, identity: string): AccountMetadata | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* An account reader for the long-running door: re-reads an identity's `.claude.json` only when its modification time or size has changed, since the door records every request and the file (which also holds Claude Code's per-project history) can be large while its account block rarely changes.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createAccountReader(fs: Pick<FarmFs, "readFileUtf8" | "lstat">, identitiesDir: string): (identity: string) => AccountMetadata | undefined;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { FarmFs } from "../launcher/ports";
|
|
2
|
+
import { type LimitKind, type UsageRecord, type UsageSnapshot } from "./schema";
|
|
3
|
+
/**
|
|
4
|
+
* The read side of the usage store: pure over an injected filesystem, so the CLI, the library surface and tests all read the same way, and nothing here writes.
|
|
5
|
+
*/
|
|
6
|
+
/** The filesystem reads the usage store's readers need. */
|
|
7
|
+
export type UsageReadFs = Pick<FarmFs, "readFileUtf8" | "readdir">;
|
|
8
|
+
/** The UTC day an instant falls on, as a segment names it. */
|
|
9
|
+
export declare function segmentDay(epochMs: number): string;
|
|
10
|
+
/** A segment's file name for one writer on one day. */
|
|
11
|
+
export declare function segmentName(day: string, pid: number): string;
|
|
12
|
+
/** One log segment found on disk. */
|
|
13
|
+
export interface LogSegment {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly day: string;
|
|
16
|
+
/** The epoch milliseconds the segment's day ends at: no record in it is later. */
|
|
17
|
+
readonly endsAt: number;
|
|
18
|
+
}
|
|
19
|
+
/** Lists the log's segments, oldest day first; files that are not segments (a stray temporary file) are not the log's and are ignored. */
|
|
20
|
+
export declare function listLogSegments(fs: Pick<FarmFs, "readdir">, logDir: string): readonly LogSegment[];
|
|
21
|
+
/** What a log read found. */
|
|
22
|
+
export interface UsageLogRead {
|
|
23
|
+
/** Every valid record at or after the cut-off, oldest first. */
|
|
24
|
+
readonly records: readonly UsageRecord[];
|
|
25
|
+
/** Complete lines that did not parse as a record of this schema version: a corrupted line, or one a newer agent-shim wrote. Reported, never silently dropped. */
|
|
26
|
+
readonly invalidLines: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Reads the usage log, optionally only from `sinceMs` on. Each segment has a single writer that appends whole lines, so the only incomplete line a reader can meet is the last one of a segment still being written, which has no trailing newline yet; it is left for the next read rather than counted as invalid.
|
|
30
|
+
*/
|
|
31
|
+
export declare function readUsageLog(fs: UsageReadFs, logDir: string, options?: {
|
|
32
|
+
readonly sinceMs?: number;
|
|
33
|
+
}): UsageLogRead;
|
|
34
|
+
/** Raised for a snapshot file that exists but does not hold a snapshot of this schema version. */
|
|
35
|
+
export declare class UsageSnapshotError extends Error {
|
|
36
|
+
readonly file: string;
|
|
37
|
+
constructor(file: string, detail: string);
|
|
38
|
+
}
|
|
39
|
+
/** The path of an identity's snapshot. Throws for an invalid identity name, which could otherwise name a path outside the snapshots directory. */
|
|
40
|
+
export declare function snapshotPath(snapshotsDir: string, identity: string): string;
|
|
41
|
+
/** Reads one identity's snapshot: undefined when it has none yet, `UsageSnapshotError` when the file is there but unreadable. */
|
|
42
|
+
export declare function readUsageSnapshot(fs: Pick<FarmFs, "readFileUtf8">, snapshotsDir: string, identity: string): UsageSnapshot | undefined;
|
|
43
|
+
/** Reads every identity's snapshot, by identity name. */
|
|
44
|
+
export declare function listUsageSnapshots(fs: UsageReadFs, snapshotsDir: string): readonly UsageSnapshot[];
|
|
45
|
+
/** Token totals across a set of records. */
|
|
46
|
+
interface TokenTotals {
|
|
47
|
+
readonly inputTokens: number;
|
|
48
|
+
readonly outputTokens: number;
|
|
49
|
+
readonly cacheCreationInputTokens: number;
|
|
50
|
+
readonly cacheReadInputTokens: number;
|
|
51
|
+
}
|
|
52
|
+
/** The totals for one identity and provider over a set of records. */
|
|
53
|
+
export interface UsageSummary {
|
|
54
|
+
/** The identity, or undefined for requests from a launch that resolved none. */
|
|
55
|
+
readonly identity: string | undefined;
|
|
56
|
+
readonly provider: string;
|
|
57
|
+
readonly requests: number;
|
|
58
|
+
/** Responses with a status of 400 or above (refusals included). */
|
|
59
|
+
readonly failed: number;
|
|
60
|
+
/** Classified refusals, by kind. */
|
|
61
|
+
readonly limits: Readonly<Record<LimitKind, number>>;
|
|
62
|
+
readonly tokens: TokenTotals;
|
|
63
|
+
readonly firstAt: string;
|
|
64
|
+
readonly lastAt: string;
|
|
65
|
+
/** The models the responses named, most-used first. */
|
|
66
|
+
readonly models: readonly string[];
|
|
67
|
+
}
|
|
68
|
+
/** Groups records by identity and provider and totals each group, ordered by identity then provider. */
|
|
69
|
+
export declare function summariseUsage(records: readonly UsageRecord[]): readonly UsageSummary[];
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The usage store's one schema definition: every record the front door appends to the usage log, the per-identity snapshot other tools read, and the account metadata both carry. Each is a strict object, so a field not named here can never be written (the store validates every record and snapshot before it touches disk) and the published JSON Schemas (`schema/UsageRecord.schema.json`, `schema/UsageSnapshot.schema.json`) describe exactly what a reader can find.
|
|
4
|
+
*
|
|
5
|
+
* Metadata only, by construction: nothing here can hold a prompt, a response, tool content, or a credential. Rate-limit headers are kept by name from an allow-list of quota headers (`isRateLimitHeader`), never wholesale.
|
|
6
|
+
*/
|
|
7
|
+
/** The version of the record and snapshot shapes below. A reader checks it before trusting a document; a change that is not purely additive bumps it. */
|
|
8
|
+
export declare const USAGE_SCHEMA_VERSION = 1;
|
|
9
|
+
/** Token counts from a response body's `usage` object, renamed to this project's casing. Each field is present only when the response reported it. */
|
|
10
|
+
export declare const TokenUsageSchema: z.ZodObject<{
|
|
11
|
+
inputTokens: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
cacheCreationInputTokens: z.ZodOptional<z.ZodNumber>;
|
|
14
|
+
cacheReadInputTokens: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
}, z.core.$strict>;
|
|
16
|
+
export type TokenUsage = z.infer<typeof TokenUsageSchema>;
|
|
17
|
+
/** The two kinds of refusal a consumer acts on differently: a short rate limit is worth retrying on the same credential once its wait has passed, an exhausted quota means moving to the next credential until the reset. */
|
|
18
|
+
declare const LIMIT_KINDS: readonly ["rate-limited", "quota-exhausted"];
|
|
19
|
+
export type LimitKind = (typeof LIMIT_KINDS)[number];
|
|
20
|
+
/** A 429 (or 402) classified, with the wait or reset the upstream announced and the signals the classification rests on. */
|
|
21
|
+
export declare const LimitClassificationSchema: z.ZodObject<{
|
|
22
|
+
kind: z.ZodEnum<{
|
|
23
|
+
"rate-limited": "rate-limited";
|
|
24
|
+
"quota-exhausted": "quota-exhausted";
|
|
25
|
+
}>;
|
|
26
|
+
retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
|
|
27
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
28
|
+
window: z.ZodOptional<z.ZodString>;
|
|
29
|
+
evidence: z.ZodArray<z.ZodString>;
|
|
30
|
+
}, z.core.$strict>;
|
|
31
|
+
export type LimitClassification = z.infer<typeof LimitClassificationSchema>;
|
|
32
|
+
/** One routed request, appended to the usage log when its response has finished. */
|
|
33
|
+
export declare const UsageRecordSchema: z.ZodObject<{
|
|
34
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
35
|
+
at: z.ZodISODateTime;
|
|
36
|
+
identity: z.ZodOptional<z.ZodString>;
|
|
37
|
+
provider: z.ZodString;
|
|
38
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
39
|
+
project: z.ZodOptional<z.ZodString>;
|
|
40
|
+
route: z.ZodString;
|
|
41
|
+
method: z.ZodString;
|
|
42
|
+
endpoint: z.ZodString;
|
|
43
|
+
model: z.ZodOptional<z.ZodString>;
|
|
44
|
+
status: z.ZodNumber;
|
|
45
|
+
latencyMs: z.ZodNumber;
|
|
46
|
+
durationMs: z.ZodNumber;
|
|
47
|
+
outcome: z.ZodEnum<{
|
|
48
|
+
completed: "completed";
|
|
49
|
+
aborted: "aborted";
|
|
50
|
+
}>;
|
|
51
|
+
usage: z.ZodOptional<z.ZodObject<{
|
|
52
|
+
inputTokens: z.ZodOptional<z.ZodNumber>;
|
|
53
|
+
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
54
|
+
cacheCreationInputTokens: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
cacheReadInputTokens: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
}, z.core.$strict>>;
|
|
57
|
+
rateLimitHeaders: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
58
|
+
limit: z.ZodOptional<z.ZodObject<{
|
|
59
|
+
kind: z.ZodEnum<{
|
|
60
|
+
"rate-limited": "rate-limited";
|
|
61
|
+
"quota-exhausted": "quota-exhausted";
|
|
62
|
+
}>;
|
|
63
|
+
retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
65
|
+
window: z.ZodOptional<z.ZodString>;
|
|
66
|
+
evidence: z.ZodArray<z.ZodString>;
|
|
67
|
+
}, z.core.$strict>>;
|
|
68
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
69
|
+
}, z.core.$strict>;
|
|
70
|
+
export type UsageRecord = z.infer<typeof UsageRecordSchema>;
|
|
71
|
+
/** One quota window from Anthropic's unified rate-limit headers. */
|
|
72
|
+
declare const QuotaWindowSchema: z.ZodObject<{
|
|
73
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
74
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
75
|
+
status: z.ZodOptional<z.ZodString>;
|
|
76
|
+
}, z.core.$strict>;
|
|
77
|
+
export type QuotaWindow = z.infer<typeof QuotaWindowSchema>;
|
|
78
|
+
/** Anthropic's unified subscription rate-limit state, parsed from the `anthropic-ratelimit-unified-*` headers (which the in-process Codex route also derives from the Codex backend's own quota headers). */
|
|
79
|
+
export declare const UnifiedRateLimitSchema: z.ZodObject<{
|
|
80
|
+
status: z.ZodOptional<z.ZodString>;
|
|
81
|
+
fiveHour: z.ZodOptional<z.ZodObject<{
|
|
82
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
83
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
84
|
+
status: z.ZodOptional<z.ZodString>;
|
|
85
|
+
}, z.core.$strict>>;
|
|
86
|
+
sevenDay: z.ZodOptional<z.ZodObject<{
|
|
87
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
88
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
89
|
+
status: z.ZodOptional<z.ZodString>;
|
|
90
|
+
}, z.core.$strict>>;
|
|
91
|
+
representativeClaim: z.ZodOptional<z.ZodString>;
|
|
92
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
93
|
+
overageStatus: z.ZodOptional<z.ZodString>;
|
|
94
|
+
}, z.core.$strict>;
|
|
95
|
+
export type UnifiedRateLimit = z.infer<typeof UnifiedRateLimitSchema>;
|
|
96
|
+
/** The latest rate-limit state an upstream reported on any response. */
|
|
97
|
+
declare const RateLimitStateSchema: z.ZodObject<{
|
|
98
|
+
observedAt: z.ZodISODateTime;
|
|
99
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
100
|
+
unified: z.ZodOptional<z.ZodObject<{
|
|
101
|
+
status: z.ZodOptional<z.ZodString>;
|
|
102
|
+
fiveHour: z.ZodOptional<z.ZodObject<{
|
|
103
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
104
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
105
|
+
status: z.ZodOptional<z.ZodString>;
|
|
106
|
+
}, z.core.$strict>>;
|
|
107
|
+
sevenDay: z.ZodOptional<z.ZodObject<{
|
|
108
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
109
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
110
|
+
status: z.ZodOptional<z.ZodString>;
|
|
111
|
+
}, z.core.$strict>>;
|
|
112
|
+
representativeClaim: z.ZodOptional<z.ZodString>;
|
|
113
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
114
|
+
overageStatus: z.ZodOptional<z.ZodString>;
|
|
115
|
+
}, z.core.$strict>>;
|
|
116
|
+
}, z.core.$strict>;
|
|
117
|
+
export type RateLimitState = z.infer<typeof RateLimitStateSchema>;
|
|
118
|
+
/** A classified refusal, with when it was seen and its status. */
|
|
119
|
+
declare const LimitEventSchema: z.ZodObject<{
|
|
120
|
+
kind: z.ZodEnum<{
|
|
121
|
+
"rate-limited": "rate-limited";
|
|
122
|
+
"quota-exhausted": "quota-exhausted";
|
|
123
|
+
}>;
|
|
124
|
+
retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
|
|
125
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
126
|
+
window: z.ZodOptional<z.ZodString>;
|
|
127
|
+
evidence: z.ZodArray<z.ZodString>;
|
|
128
|
+
observedAt: z.ZodISODateTime;
|
|
129
|
+
status: z.ZodNumber;
|
|
130
|
+
}, z.core.$strict>;
|
|
131
|
+
export type LimitEvent = z.infer<typeof LimitEventSchema>;
|
|
132
|
+
/** One quota window a provider reports through its own usage endpoint (as opposed to headers on a response): how much of it is used and when it resets. */
|
|
133
|
+
declare const ProviderQuotaWindowSchema: z.ZodObject<{
|
|
134
|
+
measures: z.ZodString;
|
|
135
|
+
periodMs: z.ZodOptional<z.ZodNumber>;
|
|
136
|
+
period: z.ZodOptional<z.ZodString>;
|
|
137
|
+
utilization: z.ZodNumber;
|
|
138
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
139
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
140
|
+
used: z.ZodOptional<z.ZodNumber>;
|
|
141
|
+
remaining: z.ZodOptional<z.ZodNumber>;
|
|
142
|
+
}, z.core.$strict>;
|
|
143
|
+
export type ProviderQuotaWindow = z.infer<typeof ProviderQuotaWindowSchema>;
|
|
144
|
+
/** A provider's quota as its usage endpoint last reported it. Pulled, not observed on a request, so it carries its own observation time. */
|
|
145
|
+
export declare const ProviderQuotaSchema: z.ZodObject<{
|
|
146
|
+
observedAt: z.ZodISODateTime;
|
|
147
|
+
source: z.ZodString;
|
|
148
|
+
level: z.ZodOptional<z.ZodString>;
|
|
149
|
+
windows: z.ZodArray<z.ZodObject<{
|
|
150
|
+
measures: z.ZodString;
|
|
151
|
+
periodMs: z.ZodOptional<z.ZodNumber>;
|
|
152
|
+
period: z.ZodOptional<z.ZodString>;
|
|
153
|
+
utilization: z.ZodNumber;
|
|
154
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
155
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
156
|
+
used: z.ZodOptional<z.ZodNumber>;
|
|
157
|
+
remaining: z.ZodOptional<z.ZodNumber>;
|
|
158
|
+
}, z.core.$strict>>;
|
|
159
|
+
}, z.core.$strict>;
|
|
160
|
+
export type ProviderQuota = z.infer<typeof ProviderQuotaSchema>;
|
|
161
|
+
/** One provider's latest state under one identity. */
|
|
162
|
+
declare const ProviderUsageStateSchema: z.ZodObject<{
|
|
163
|
+
lastRequestAt: z.ZodISODateTime;
|
|
164
|
+
lastStatus: z.ZodNumber;
|
|
165
|
+
lastModel: z.ZodOptional<z.ZodString>;
|
|
166
|
+
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
167
|
+
observedAt: z.ZodISODateTime;
|
|
168
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
169
|
+
unified: z.ZodOptional<z.ZodObject<{
|
|
170
|
+
status: z.ZodOptional<z.ZodString>;
|
|
171
|
+
fiveHour: z.ZodOptional<z.ZodObject<{
|
|
172
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
173
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
174
|
+
status: z.ZodOptional<z.ZodString>;
|
|
175
|
+
}, z.core.$strict>>;
|
|
176
|
+
sevenDay: z.ZodOptional<z.ZodObject<{
|
|
177
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
178
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
179
|
+
status: z.ZodOptional<z.ZodString>;
|
|
180
|
+
}, z.core.$strict>>;
|
|
181
|
+
representativeClaim: z.ZodOptional<z.ZodString>;
|
|
182
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
183
|
+
overageStatus: z.ZodOptional<z.ZodString>;
|
|
184
|
+
}, z.core.$strict>>;
|
|
185
|
+
}, z.core.$strict>>;
|
|
186
|
+
lastLimit: z.ZodOptional<z.ZodObject<{
|
|
187
|
+
kind: z.ZodEnum<{
|
|
188
|
+
"rate-limited": "rate-limited";
|
|
189
|
+
"quota-exhausted": "quota-exhausted";
|
|
190
|
+
}>;
|
|
191
|
+
retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
|
|
192
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
193
|
+
window: z.ZodOptional<z.ZodString>;
|
|
194
|
+
evidence: z.ZodArray<z.ZodString>;
|
|
195
|
+
observedAt: z.ZodISODateTime;
|
|
196
|
+
status: z.ZodNumber;
|
|
197
|
+
}, z.core.$strict>>;
|
|
198
|
+
quota: z.ZodOptional<z.ZodObject<{
|
|
199
|
+
observedAt: z.ZodISODateTime;
|
|
200
|
+
source: z.ZodString;
|
|
201
|
+
level: z.ZodOptional<z.ZodString>;
|
|
202
|
+
windows: z.ZodArray<z.ZodObject<{
|
|
203
|
+
measures: z.ZodString;
|
|
204
|
+
periodMs: z.ZodOptional<z.ZodNumber>;
|
|
205
|
+
period: z.ZodOptional<z.ZodString>;
|
|
206
|
+
utilization: z.ZodNumber;
|
|
207
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
208
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
209
|
+
used: z.ZodOptional<z.ZodNumber>;
|
|
210
|
+
remaining: z.ZodOptional<z.ZodNumber>;
|
|
211
|
+
}, z.core.$strict>>;
|
|
212
|
+
}, z.core.$strict>>;
|
|
213
|
+
}, z.core.$strict>;
|
|
214
|
+
export type ProviderUsageState = z.infer<typeof ProviderUsageStateSchema>;
|
|
215
|
+
/**
|
|
216
|
+
* The account fields copied from an identity's stored Claude login (`oauthAccount` in the identity's `.claude.json`): who the account is and which plan and tier it is on. Never the token, which Claude Code keeps elsewhere. Each field is present only when the stored login has it; a setup-token identity has no profile scope and so none of them (see issue #44).
|
|
217
|
+
*/
|
|
218
|
+
export declare const AccountMetadataSchema: z.ZodObject<{
|
|
219
|
+
accountUuid: z.ZodOptional<z.ZodString>;
|
|
220
|
+
emailAddress: z.ZodOptional<z.ZodString>;
|
|
221
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
222
|
+
organizationUuid: z.ZodOptional<z.ZodString>;
|
|
223
|
+
organizationName: z.ZodOptional<z.ZodString>;
|
|
224
|
+
organizationType: z.ZodOptional<z.ZodString>;
|
|
225
|
+
organizationRole: z.ZodOptional<z.ZodString>;
|
|
226
|
+
workspaceRole: z.ZodOptional<z.ZodString>;
|
|
227
|
+
billingType: z.ZodOptional<z.ZodString>;
|
|
228
|
+
seatTier: z.ZodOptional<z.ZodString>;
|
|
229
|
+
organizationRateLimitTier: z.ZodOptional<z.ZodString>;
|
|
230
|
+
userRateLimitTier: z.ZodOptional<z.ZodString>;
|
|
231
|
+
hasExtraUsageEnabled: z.ZodOptional<z.ZodBoolean>;
|
|
232
|
+
subscriptionCreatedAt: z.ZodOptional<z.ZodString>;
|
|
233
|
+
accountCreatedAt: z.ZodOptional<z.ZodString>;
|
|
234
|
+
}, z.core.$strict>;
|
|
235
|
+
export type AccountMetadata = z.infer<typeof AccountMetadataSchema>;
|
|
236
|
+
/**
|
|
237
|
+
* The latest state for one identity, rewritten atomically after each recorded response: the snapshot file other tools read (`~/.agent-shim/usage/snapshots/<identity>.json`) without shelling out to `agent-shim`.
|
|
238
|
+
*/
|
|
239
|
+
export declare const UsageSnapshotSchema: z.ZodObject<{
|
|
240
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
241
|
+
identity: z.ZodString;
|
|
242
|
+
updatedAt: z.ZodISODateTime;
|
|
243
|
+
account: z.ZodOptional<z.ZodObject<{
|
|
244
|
+
accountUuid: z.ZodOptional<z.ZodString>;
|
|
245
|
+
emailAddress: z.ZodOptional<z.ZodString>;
|
|
246
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
247
|
+
organizationUuid: z.ZodOptional<z.ZodString>;
|
|
248
|
+
organizationName: z.ZodOptional<z.ZodString>;
|
|
249
|
+
organizationType: z.ZodOptional<z.ZodString>;
|
|
250
|
+
organizationRole: z.ZodOptional<z.ZodString>;
|
|
251
|
+
workspaceRole: z.ZodOptional<z.ZodString>;
|
|
252
|
+
billingType: z.ZodOptional<z.ZodString>;
|
|
253
|
+
seatTier: z.ZodOptional<z.ZodString>;
|
|
254
|
+
organizationRateLimitTier: z.ZodOptional<z.ZodString>;
|
|
255
|
+
userRateLimitTier: z.ZodOptional<z.ZodString>;
|
|
256
|
+
hasExtraUsageEnabled: z.ZodOptional<z.ZodBoolean>;
|
|
257
|
+
subscriptionCreatedAt: z.ZodOptional<z.ZodString>;
|
|
258
|
+
accountCreatedAt: z.ZodOptional<z.ZodString>;
|
|
259
|
+
}, z.core.$strict>>;
|
|
260
|
+
providers: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
261
|
+
lastRequestAt: z.ZodISODateTime;
|
|
262
|
+
lastStatus: z.ZodNumber;
|
|
263
|
+
lastModel: z.ZodOptional<z.ZodString>;
|
|
264
|
+
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
265
|
+
observedAt: z.ZodISODateTime;
|
|
266
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
267
|
+
unified: z.ZodOptional<z.ZodObject<{
|
|
268
|
+
status: z.ZodOptional<z.ZodString>;
|
|
269
|
+
fiveHour: z.ZodOptional<z.ZodObject<{
|
|
270
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
271
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
272
|
+
status: z.ZodOptional<z.ZodString>;
|
|
273
|
+
}, z.core.$strict>>;
|
|
274
|
+
sevenDay: z.ZodOptional<z.ZodObject<{
|
|
275
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
276
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
277
|
+
status: z.ZodOptional<z.ZodString>;
|
|
278
|
+
}, z.core.$strict>>;
|
|
279
|
+
representativeClaim: z.ZodOptional<z.ZodString>;
|
|
280
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
281
|
+
overageStatus: z.ZodOptional<z.ZodString>;
|
|
282
|
+
}, z.core.$strict>>;
|
|
283
|
+
}, z.core.$strict>>;
|
|
284
|
+
lastLimit: z.ZodOptional<z.ZodObject<{
|
|
285
|
+
kind: z.ZodEnum<{
|
|
286
|
+
"rate-limited": "rate-limited";
|
|
287
|
+
"quota-exhausted": "quota-exhausted";
|
|
288
|
+
}>;
|
|
289
|
+
retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
|
|
290
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
291
|
+
window: z.ZodOptional<z.ZodString>;
|
|
292
|
+
evidence: z.ZodArray<z.ZodString>;
|
|
293
|
+
observedAt: z.ZodISODateTime;
|
|
294
|
+
status: z.ZodNumber;
|
|
295
|
+
}, z.core.$strict>>;
|
|
296
|
+
quota: z.ZodOptional<z.ZodObject<{
|
|
297
|
+
observedAt: z.ZodISODateTime;
|
|
298
|
+
source: z.ZodString;
|
|
299
|
+
level: z.ZodOptional<z.ZodString>;
|
|
300
|
+
windows: z.ZodArray<z.ZodObject<{
|
|
301
|
+
measures: z.ZodString;
|
|
302
|
+
periodMs: z.ZodOptional<z.ZodNumber>;
|
|
303
|
+
period: z.ZodOptional<z.ZodString>;
|
|
304
|
+
utilization: z.ZodNumber;
|
|
305
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
306
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
307
|
+
used: z.ZodOptional<z.ZodNumber>;
|
|
308
|
+
remaining: z.ZodOptional<z.ZodNumber>;
|
|
309
|
+
}, z.core.$strict>>;
|
|
310
|
+
}, z.core.$strict>>;
|
|
311
|
+
}, z.core.$strict>>;
|
|
312
|
+
}, z.core.$strict>;
|
|
313
|
+
export type UsageSnapshot = z.infer<typeof UsageSnapshotSchema>;
|
|
314
|
+
/**
|
|
315
|
+
* Whether a response header is one the store keeps: the quota and rate-limit families the upstreams agent-shim routes to send (Anthropic's `anthropic-ratelimit-*`, the IETF draft's `ratelimit*`, the common `x-ratelimit-*`, the Codex backend's `x-codex-*` quota headers, which the Codex route forwards) and `retry-after`. Every one of these carries a number, a time or a status word; none carries a credential or content.
|
|
316
|
+
*/
|
|
317
|
+
export declare function isRateLimitHeader(name: string): boolean;
|
|
318
|
+
export {};
|