agent-shim 7.13.1 → 7.14.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/cli.cjs +95 -27
- package/dist/index.cjs +70 -7
- package/dist/index.mjs +70 -7
- package/dist/types/index.d.ts +2789 -40
- package/package.json +3 -1
- package/dist/types/checkReport.d.ts +0 -154
- package/dist/types/claudeShim.d.ts +0 -114
- package/dist/types/cli/bool.d.ts +0 -8
- package/dist/types/cli/commandDeps.d.ts +0 -39
- package/dist/types/cli/envBool.d.ts +0 -11
- package/dist/types/cli/pairs.d.ts +0 -19
- package/dist/types/cliError.d.ts +0 -27
- package/dist/types/codex/http.d.ts +0 -15
- package/dist/types/config/classify.d.ts +0 -49
- package/dist/types/config/load.d.ts +0 -42
- package/dist/types/config/schema.d.ts +0 -746
- package/dist/types/config/store.d.ts +0 -46
- package/dist/types/configProfilesStore.d.ts +0 -61
- package/dist/types/configure.d.ts +0 -170
- package/dist/types/credential.d.ts +0 -136
- package/dist/types/credentialCache.d.ts +0 -46
- package/dist/types/directoryRulesStore.d.ts +0 -43
- package/dist/types/doctorReport.d.ts +0 -117
- package/dist/types/frontdoor/capability.d.ts +0 -19
- package/dist/types/frontdoor/capture.d.ts +0 -58
- package/dist/types/frontdoor/connect.d.ts +0 -209
- package/dist/types/frontdoor/connectEffects.d.ts +0 -3
- package/dist/types/frontdoor/custody.d.ts +0 -32
- package/dist/types/frontdoor/ensure.d.ts +0 -36
- package/dist/types/frontdoor/headroomHop.d.ts +0 -22
- package/dist/types/frontdoor/pipeline.d.ts +0 -109
- package/dist/types/frontdoor/probe.d.ts +0 -13
- package/dist/types/frontdoor/realFrontDoorPort.d.ts +0 -5
- package/dist/types/frontdoor/route.d.ts +0 -112
- package/dist/types/frontdoor/server.d.ts +0 -44
- package/dist/types/frontdoor/state.d.ts +0 -57
- package/dist/types/frontdoor/supervisor.d.ts +0 -48
- package/dist/types/frontdoor/tap.d.ts +0 -9
- package/dist/types/frontdoor/trust.d.ts +0 -30
- package/dist/types/frontdoor/x509.d.ts +0 -11
- package/dist/types/headroom/ensure.d.ts +0 -31
- package/dist/types/headroom/headers.d.ts +0 -11
- package/dist/types/headroom/settings.d.ts +0 -13
- package/dist/types/headroom/source.d.ts +0 -8
- package/dist/types/headroom/state.d.ts +0 -77
- package/dist/types/headroom/supervisor.d.ts +0 -107
- package/dist/types/identityStore.d.ts +0 -85
- package/dist/types/launchWiring.d.ts +0 -49
- package/dist/types/launcher/argv.d.ts +0 -42
- package/dist/types/launcher/cascade.d.ts +0 -42
- package/dist/types/launcher/cliOverride.d.ts +0 -38
- package/dist/types/launcher/extraFlags.d.ts +0 -6
- package/dist/types/launcher/farm.d.ts +0 -168
- package/dist/types/launcher/flags.d.ts +0 -96
- package/dist/types/launcher/guard.d.ts +0 -57
- package/dist/types/launcher/identity.d.ts +0 -95
- package/dist/types/launcher/lock.d.ts +0 -41
- package/dist/types/launcher/pool.d.ts +0 -44
- package/dist/types/launcher/ports.d.ts +0 -135
- package/dist/types/launcher/spawn.d.ts +0 -20
- package/dist/types/launcher.d.ts +0 -98
- package/dist/types/legacy.d.ts +0 -29
- package/dist/types/pathNorm.d.ts +0 -14
- package/dist/types/paths.d.ts +0 -79
- package/dist/types/poolPickReport.d.ts +0 -44
- package/dist/types/poolStore.d.ts +0 -18
- package/dist/types/providersStore.d.ts +0 -137
- package/dist/types/realCredentialCache.d.ts +0 -11
- package/dist/types/realPorts.d.ts +0 -109
- package/dist/types/resolve/conditions.d.ts +0 -37
- package/dist/types/resolve/decide.d.ts +0 -45
- package/dist/types/resolve/extends.d.ts +0 -41
- package/dist/types/resolve/flatten.d.ts +0 -13
- package/dist/types/resolve/match.d.ts +0 -58
- package/dist/types/resolve/pipeline.d.ts +0 -33
- package/dist/types/resolve/plan.d.ts +0 -57
- package/dist/types/resolve/projects.d.ts +0 -61
- package/dist/types/resolve/reconcile.d.ts +0 -80
- package/dist/types/resolve/types.d.ts +0 -159
- package/dist/types/resolve/walk.d.ts +0 -85
- package/dist/types/resolve/walkPolicy.d.ts +0 -15
- package/dist/types/usage/account.d.ts +0 -20
- package/dist/types/usage/middleware.d.ts +0 -17
- package/dist/types/usage/pick.d.ts +0 -62
- package/dist/types/usage/plan.d.ts +0 -18
- package/dist/types/usage/poolPick.d.ts +0 -31
- package/dist/types/usage/preflight.d.ts +0 -20
- package/dist/types/usage/rateLimit.d.ts +0 -25
- package/dist/types/usage/read.d.ts +0 -70
- package/dist/types/usage/scan.d.ts +0 -24
- package/dist/types/usage/schema.d.ts +0 -318
- package/dist/types/usage/store.d.ts +0 -43
- package/dist/types/versionDiscovery.d.ts +0 -47
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Forward-only encoding of a real working directory into the single directory name Claude Code gives it under `~/.claude/projects/`.
|
|
3
|
-
*
|
|
4
|
-
* The encoding collapses the *entire* non-alphanumeric character class to `-`, not just the path separator: `.`, `_`, ` `, `@`, and `/` all become `-`, while letters and digits pass through with their case preserved. This was confirmed against a real installation's project directories, not assumed from the one `/`-becomes-`-` sample the README quotes.
|
|
5
|
-
*
|
|
6
|
-
* The encoding is therefore many-to-one: `~/work/clients/acme`, `~/work/clients-acme`, and `~/work-clients/acme` all produce the identical name. That makes it impossible to invert, so this module never tries: there is no decode function here, and `projects.test.ts` asserts the module's export list to keep it that way. Only the forward direction — real path to encoded name — is ever computed, and `detectEncodingAmbiguity` reports when a pattern's encoded form could plausibly correspond to more than one real path rather than resolving it silently.
|
|
7
|
-
*/
|
|
8
|
-
import { CliError } from "../cliError";
|
|
9
|
-
/** Encodes one real, fully-literal path into the directory name Claude Code would give it under `~/.claude/projects/`. */
|
|
10
|
-
export declare function encodeProjectPath(realPath: string): string;
|
|
11
|
-
/** One piece of a pattern split on its wildcard tokens: either a literal run to encode, or a wildcard to leave alone. */
|
|
12
|
-
export interface PatternFragment {
|
|
13
|
-
readonly kind: "literal" | "wildcard";
|
|
14
|
-
readonly text: string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Splits a pattern into alternating literal runs and wildcard tokens. Encoding the whole pattern string in one pass would turn `*` into `-` along with everything else, silently converting a glob into a literal that matches nothing.
|
|
18
|
-
*/
|
|
19
|
-
export declare function splitOnWildcards(pattern: string): PatternFragment[];
|
|
20
|
-
/** Raised when a `history/projects/` pattern's path fragment is neither `~`-rooted nor absolute, so there is no real path to encode. */
|
|
21
|
-
export declare class UnrootedProjectPathError extends CliError {
|
|
22
|
-
readonly fragment: string;
|
|
23
|
-
constructor(fragment: string);
|
|
24
|
-
}
|
|
25
|
-
/** Expands a leading `~` to the given home directory. A `~` anywhere else in the path is an ordinary character. */
|
|
26
|
-
export declare function expandHome(fragment: string, home: string): string;
|
|
27
|
-
/**
|
|
28
|
-
* Encodes a possibly-globbed real path into a pattern that matches Claude Code's own `~/.claude/projects/` directory names.
|
|
29
|
-
*
|
|
30
|
-
* Order matters and is fixed: expand `~` to the real home first (encoding `~` would turn it into `-` and lose the reference), reject anything not home-or-root-rooted, then split on wildcard tokens and encode only the literal runs.
|
|
31
|
-
*/
|
|
32
|
-
export declare function encodeProjectPattern(fragment: string, options: Readonly<{
|
|
33
|
-
home: string;
|
|
34
|
-
}>): string;
|
|
35
|
-
/** Why a pattern's encoded form might not identify the real path its author had in mind. */
|
|
36
|
-
export type EncodingAmbiguityReason =
|
|
37
|
-
/** The literal portion contains a character other than `/` that encodes to `-`, so a sibling path differing only in that character encodes identically. */
|
|
38
|
-
"lossy-characters"
|
|
39
|
-
/** Two different patterns in the same configuration encode to the identical form. */
|
|
40
|
-
| "collides-with-sibling-pattern";
|
|
41
|
-
/** One reported ambiguity in a `history/projects/` pattern. */
|
|
42
|
-
export interface EncodingAmbiguity {
|
|
43
|
-
readonly fragment: string;
|
|
44
|
-
readonly encoded: string;
|
|
45
|
-
readonly reason: EncodingAmbiguityReason;
|
|
46
|
-
readonly detail: string;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Reports, for a set of `history/projects/` path fragments, every case where the encoded form could plausibly correspond to more than one real path.
|
|
50
|
-
*
|
|
51
|
-
* Two signals, both actionable:
|
|
52
|
-
*
|
|
53
|
-
* - `lossy-characters` — the fragment's literal portion contains a character besides `/` that encodes to `-`. Because encoding is many-to-one, a sibling path with that character replaced by a separator (or vice versa) produces the identical name, so this pattern may match a directory belonging to a different real path.
|
|
54
|
-
* - `collides-with-sibling-pattern` — two distinct fragments in the same configuration encode identically, which proves a collision outright rather than merely suggesting one.
|
|
55
|
-
*
|
|
56
|
-
* When `existingNames` is supplied, `detail` also names how many real project directories the encoded form currently matches, so the report says what actually happened rather than only what could.
|
|
57
|
-
*/
|
|
58
|
-
export declare function detectEncodingAmbiguity(fragments: readonly string[], options: {
|
|
59
|
-
home: string;
|
|
60
|
-
existingNames?: readonly string[];
|
|
61
|
-
}): EncodingAmbiguity[];
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { Diagnostic } from "./types";
|
|
2
|
-
/** The manifest the farm builder writes into each scratch tree before the atomic swap. */
|
|
3
|
-
export interface FarmManifest {
|
|
4
|
-
readonly version: 1;
|
|
5
|
-
readonly builtAtMs: number;
|
|
6
|
-
readonly identity: string;
|
|
7
|
-
readonly configProfile?: string;
|
|
8
|
-
readonly cwd: string;
|
|
9
|
-
readonly claudeHome: string;
|
|
10
|
-
/** Directories the previous resync built itself. Without this list, a directory the farm materialised and a directory Claude Code created are indistinguishable on disk — both are real, both contain real children. */
|
|
11
|
-
readonly materialised: readonly string[];
|
|
12
|
-
readonly links: readonly {
|
|
13
|
-
readonly rel: string;
|
|
14
|
-
readonly target: string;
|
|
15
|
-
}[];
|
|
16
|
-
}
|
|
17
|
-
/** One node of a directory listing, relative to the tree's own root. */
|
|
18
|
-
export interface ListingEntry {
|
|
19
|
-
/** Path relative to the tree root, forward-slash separated. */
|
|
20
|
-
readonly rel: string;
|
|
21
|
-
readonly kind: "dir" | "file" | "symlink";
|
|
22
|
-
/** A content hash for a file, used to recognise data that has already been adopted. Absent for directories and symlinks. */
|
|
23
|
-
readonly contentHash?: string;
|
|
24
|
-
}
|
|
25
|
-
/** What to do about one path found in the old farm. */
|
|
26
|
-
export type ReconcileAction =
|
|
27
|
-
/** New data Claude Code wrote that the canonical tree does not have. Copy it to `to`. */
|
|
28
|
-
{
|
|
29
|
-
readonly kind: "adopt";
|
|
30
|
-
readonly rel: string;
|
|
31
|
-
readonly reason: "absent-from-canonical";
|
|
32
|
-
}
|
|
33
|
-
/** New data whose canonical counterpart exists but differs. The canonical copy is authoritative; the farm copy is preserved alongside it for the user to reconcile. */
|
|
34
|
-
| {
|
|
35
|
-
readonly kind: "adopt-conflict";
|
|
36
|
-
readonly rel: string;
|
|
37
|
-
}
|
|
38
|
-
/** Nothing to do, with the reason recorded so `agent-shim check` can explain a no-op. */
|
|
39
|
-
| {
|
|
40
|
-
readonly kind: "skip";
|
|
41
|
-
readonly rel: string;
|
|
42
|
-
readonly reason: SkipReason;
|
|
43
|
-
};
|
|
44
|
-
/** Why a path in the old farm needs no action. */
|
|
45
|
-
type SkipReason =
|
|
46
|
-
/** A symlink the previous resync placed. It is a view of the canonical tree, so there is nothing to write back. */
|
|
47
|
-
"is-symlink"
|
|
48
|
-
/** A directory the previous resync materialised. Not new data itself; its children are examined individually. */
|
|
49
|
-
| "tracked-materialised-dir"
|
|
50
|
-
/** Real data whose canonical counterpart already holds identical content — already adopted, possibly by an earlier run that then crashed before the swap. */
|
|
51
|
-
| "identical-content";
|
|
52
|
-
/** The result of planning a reconciliation pass. */
|
|
53
|
-
export interface ReconciliationPlan {
|
|
54
|
-
readonly actions: readonly ReconcileAction[];
|
|
55
|
-
readonly diagnostics: readonly Diagnostic[];
|
|
56
|
-
/** True when the manifest was missing or unusable and every real directory in the farm was treated as materialised. */
|
|
57
|
-
readonly conservative: boolean;
|
|
58
|
-
}
|
|
59
|
-
/** Everything the reconciliation planner reads. It performs no I/O of its own — the launcher executes the actions it returns. */
|
|
60
|
-
export interface ReconcileParams {
|
|
61
|
-
/** The previous resync's manifest, or undefined when it is missing or failed to parse. */
|
|
62
|
-
readonly manifest?: FarmManifest;
|
|
63
|
-
/** A flat listing of the old, still-live farm. */
|
|
64
|
-
readonly farmListing: readonly ListingEntry[];
|
|
65
|
-
/** A flat listing of the canonical `~/.claude`. */
|
|
66
|
-
readonly canonicalListing: readonly ListingEntry[];
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Plans the write-through reconciliation pass that runs before each resync builds its new scratch tree.
|
|
70
|
-
*
|
|
71
|
-
* A materialised directory stops being a live view of `~/.claude` and becomes a locally-built directory of symlinks frozen at resync time. Anything Claude Code writes into it afterwards — a brand-new project subdirectory, a new session file inside an existing one — lands as a real, untracked child: invisible to every other identity, and liable to be read as stale scaffolding and pruned by a later resync. This pass finds that data and copies it back into the canonical `~/.claude`, where it belongs, before anything else happens.
|
|
72
|
-
*
|
|
73
|
-
* The manifest is what makes this possible at all. A directory the farm materialised and a directory Claude Code created look identical on disk, so without the previous resync's own record of what it built, there is no way to tell the two apart. When the manifest is missing or unreadable, the pass falls back to **conservative mode** — every real directory in the farm is treated as materialised, so every real child anywhere becomes an adopt candidate — and reports it. That is deliberately over-eager rather than a silent no-op: over-adopting copies data into the place it was always meant to live, while under-adopting loses it.
|
|
74
|
-
*
|
|
75
|
-
* The pass is deliberately independent of the *new* resolved state. A directory whose new decision is `omit`, or one collapsing back from materialised to a plain symlink, still gets reconciled first — those are precisely the cases where the data would otherwise be discarded, which is the whole reason this exists.
|
|
76
|
-
*
|
|
77
|
-
* It is also idempotent: re-running against the same old farm after a crash mid-resync produces only `skip("identical-content")` for anything the interrupted run already copied across.
|
|
78
|
-
*/
|
|
79
|
-
export declare function planReconciliation(params: ReconcileParams): ReconciliationPlan;
|
|
80
|
-
export {};
|
|
@@ -1,159 +0,0 @@
|
|
|
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 {};
|
|
@@ -1,85 +0,0 @@
|
|
|
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;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { CompiledRule } from "./types";
|
|
2
|
-
/**
|
|
3
|
-
* Whether the walk that builds entry facts must enter the directory at `dirRel` (relative to `~/.claude`). A directory it does not enter is one entry: nothing recorded below it, because nothing below it can be decided differently from the directory itself.
|
|
4
|
-
*/
|
|
5
|
-
export type DescendPolicy = (dirRel: string) => boolean;
|
|
6
|
-
/**
|
|
7
|
-
* The walk policy for a flattened cascade. A directory is entered only when some rule can decide a path inside it differently from the directory itself, so a configuration with no rule below a top-level entry reads that entry with one `lstat`; with a rule that reads subtree aggregates anywhere, every directory is entered.
|
|
8
|
-
*
|
|
9
|
-
* Why this is safe: a rule matching a directory also matches everything beneath it (see `compileMatcher`), and the category toggles decide by top-level entry, so a subtree no rule reaches into resolves to one uniform decision, which the farm plan collapses to a single link either way.
|
|
10
|
-
*/
|
|
11
|
-
export declare function descendPolicy(rules: Readonly<Iterable<CompiledRule>>): DescendPolicy;
|
|
12
|
-
/**
|
|
13
|
-
* `policy`, widened to also enter `target` and every directory on the way to it, for a caller that needs the direct children of that path recorded (the interactive picker). With no `target` it is `policy` itself.
|
|
14
|
-
*/
|
|
15
|
-
export declare function enterRulesAnd(policy: DescendPolicy, target: string | undefined): DescendPolicy;
|
|
@@ -1,20 +0,0 @@
|
|
|
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;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { ResponseObserver } from "../frontdoor/pipeline";
|
|
2
|
-
import { type UsageRecord } from "./schema";
|
|
3
|
-
/** The provider name an OAuth session's requests are recorded under: they go to Claude Code's own API. */
|
|
4
|
-
export declare const ANTHROPIC_PROVIDER = "anthropic";
|
|
5
|
-
/** Everything the usage middleware depends on, injected so it runs against fakes in tests. */
|
|
6
|
-
export interface UsageMiddlewareDeps {
|
|
7
|
-
/** Persists one finished request's record. It may throw; the middleware catches and logs, so a store failure never reaches a request. */
|
|
8
|
-
readonly record: (record: UsageRecord) => void;
|
|
9
|
-
/** Runs work after the current turn of the event loop, so recording never sits between a route's `end` and whatever the route does next (production: `setImmediate`). */
|
|
10
|
-
readonly defer: (task: () => void) => void;
|
|
11
|
-
readonly now: () => number;
|
|
12
|
-
readonly log: (line: string) => void;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* The usage-tracking response middleware: registered at the front door's hook point, it records one metadata-only `UsageRecord` per routed response once its body has finished. The head gives the status, latency and rate-limit headers; the body, scanned incrementally as the client receives it (decompressing a copy when the upstream compressed it), gives the model and token usage. The response the client receives is never touched: the middleware sees each chunk only after the socket has it, keeps no body, and does its recording on a deferred turn. Every failure (a scanner, a decompressor, the store) is caught and logged, never thrown into the request.
|
|
16
|
-
*/
|
|
17
|
-
export declare function createUsageMiddleware(deps: UsageMiddlewareDeps): ResponseObserver;
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { type PlanClass } from "./plan";
|
|
2
|
-
import type { AccountMetadata, UsageRecord, UsageSnapshot } from "./schema";
|
|
3
|
-
/** The lengths of Anthropic's two subscription windows, used when a window reported no reset time of its own. */
|
|
4
|
-
export declare const FIVE_HOUR_WINDOW_MS: number;
|
|
5
|
-
/** Anthropic's longest prompt-cache lifetime (the one-hour tier): a conversation resumed within it still has a warm cache on the account that served it, and the cache is per organisation, so it is lost by switching. */
|
|
6
|
-
export declare const PROMPT_CACHE_TTL_MS: number;
|
|
7
|
-
/** The state a member's ranking was built from. */
|
|
8
|
-
export interface PoolMember {
|
|
9
|
-
readonly identity: string;
|
|
10
|
-
/** The identity's usage snapshot, when it has one. */
|
|
11
|
-
readonly snapshot?: UsageSnapshot;
|
|
12
|
-
/** Why the member's recorded state could not be read (a corrupt or newer-schema snapshot, an unreadable login file), when that is the case. */
|
|
13
|
-
readonly readError?: string;
|
|
14
|
-
readonly account?: AccountMetadata;
|
|
15
|
-
/** The member's usage-log records from the current five-hour window on. */
|
|
16
|
-
readonly records: readonly UsageRecord[];
|
|
17
|
-
}
|
|
18
|
-
/** The member last picked for this directory, kept so a conversation stays on the account whose prompt cache is warm. */
|
|
19
|
-
export interface StickyPick {
|
|
20
|
-
readonly identity: string;
|
|
21
|
-
readonly at: string;
|
|
22
|
-
}
|
|
23
|
-
export interface RankPoolInput {
|
|
24
|
-
readonly members: readonly PoolMember[];
|
|
25
|
-
readonly nowMs: number;
|
|
26
|
-
readonly sticky?: StickyPick;
|
|
27
|
-
/** True when the launch continues or resumes a conversation, which belongs on the account it started on whatever the cache lifetime. */
|
|
28
|
-
readonly resuming: boolean;
|
|
29
|
-
}
|
|
30
|
-
/** In pick order. `scored` has usable quota data; `unknown` has none (never recorded, or unreadable); `pay-per-use` bills by use instead of drawing on a plan; `ineligible` is refused right now. */
|
|
31
|
-
type CandidateClass = "scored" | "unknown" | "pay-per-use" | "ineligible";
|
|
32
|
-
export interface Candidate {
|
|
33
|
-
readonly identity: string;
|
|
34
|
-
readonly class: CandidateClass;
|
|
35
|
-
/** Plan-size-weighted remaining quota per hour until its reset: higher is more wasted if left unused. Present for `scored` only. */
|
|
36
|
-
readonly score?: number;
|
|
37
|
-
/** False when the five-hour window would run dry, at the observed pace, before it resets. A `scored` candidate that is not feasible ranks below every feasible one. */
|
|
38
|
-
readonly feasible: boolean;
|
|
39
|
-
/** When an `ineligible` candidate can be used again. */
|
|
40
|
-
readonly blockedUntilMs?: number;
|
|
41
|
-
readonly plan: PlanClass;
|
|
42
|
-
/** Human-readable facts behind the class and score, in the order they matter. */
|
|
43
|
-
readonly reasons: readonly string[];
|
|
44
|
-
}
|
|
45
|
-
export interface PoolRanking {
|
|
46
|
-
/** Every member, best first. */
|
|
47
|
-
readonly candidates: readonly Candidate[];
|
|
48
|
-
/** The member to launch on: the best one that is not refused. Undefined when every member is refused. */
|
|
49
|
-
readonly pick?: Candidate;
|
|
50
|
-
/** When nothing can be picked, the soonest any member comes back. */
|
|
51
|
-
readonly earliestReturn?: {
|
|
52
|
-
readonly identity: string;
|
|
53
|
-
readonly atMs: number;
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Ranks `members` for a launch.
|
|
58
|
-
*
|
|
59
|
-
* A member is `ineligible` while a window of its plan, or a refusal it last hit, still binds; it is `unknown` without recorded quota, `pay-per-use` when its usage bills by use, and otherwise `scored`. Scored members order by how much plan-size-weighted quota would expire unused per hour of runway, with any whose five-hour window would run dry at the observed pace (their own, or the person's pace on another member rescaled by plan size) placed behind those that would not. The member last picked for this directory then moves to the front if it is still usable and either the launch resumes a conversation or its prompt cache is still warm.
|
|
60
|
-
*/
|
|
61
|
-
export declare function rankPool(input: RankPoolInput): PoolRanking;
|
|
62
|
-
export {};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { AccountMetadata } from "./schema";
|
|
2
|
-
/** What an account's plan means for picking it: a subscription with a capacity relative to the smallest paid tier, or an account billed by use. */
|
|
3
|
-
export type PlanClass = {
|
|
4
|
-
readonly kind: "subscription";
|
|
5
|
-
/** The plan's size as a multiple of the smallest subscription tier (Max 5x is 5, Max 20x is 20). */
|
|
6
|
-
readonly capacity: number;
|
|
7
|
-
/** False when the tier string named no multiplier and no tier this project knows, so `capacity` is the neutral 1 rather than a reading. */
|
|
8
|
-
readonly recognised: boolean;
|
|
9
|
-
/** The tier string the classification rests on, when the account had one. */
|
|
10
|
-
readonly tier?: string;
|
|
11
|
-
} | {
|
|
12
|
-
readonly kind: "pay-per-use";
|
|
13
|
-
readonly tier?: string;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Classifies an account from the rate-limit tier of its stored login (`organizationRateLimitTier`, which decides the organisation's limits, then `userRateLimitTier`). The tier strings are free text on Anthropic's side, so only the shapes seen are read: a trailing `<n>x` is that multiple, a bare `pro` is 1, a `zero` tier is pay-per-use. Anything else is a subscription of unrecognised size, counted as 1 and marked so the ranking can say it guessed.
|
|
17
|
-
*/
|
|
18
|
-
export declare function planOf(account: AccountMetadata | undefined): PlanClass;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { FarmFs } from "../launcher/ports";
|
|
2
|
-
import type { LayoutPaths } from "../paths";
|
|
3
|
-
import { type PoolMember, type PoolRanking, type StickyPick } from "./pick";
|
|
4
|
-
import { type UsageReadFs } from "./read";
|
|
5
|
-
/**
|
|
6
|
-
* The store-backed side of pool picking: loads each member's recorded state and ranks it with `rankPool`, and keeps the per-directory record of the last pick. The launcher and `agent-shim pool pick` both go through `rankPoolFromStore`, so the command shows exactly what a launch would choose.
|
|
7
|
-
*/
|
|
8
|
-
/** The reads a pool ranking needs: snapshots, the log and each member's login file. */
|
|
9
|
-
export type PoolPickFs = UsageReadFs & Pick<FarmFs, "readFileUtf8">;
|
|
10
|
-
type PoolPickPaths = Pick<LayoutPaths, "identitiesDir" | "usageLogDir" | "usageSnapshotsDir">;
|
|
11
|
-
/** Reads each member's snapshot, login metadata and the part of the log that reaches into the current five-hour window. A member whose state cannot be read is carried with the reason instead of failing the pick. */
|
|
12
|
-
export declare function loadPoolMembers(fs: PoolPickFs, paths: PoolPickPaths, identities: readonly string[], nowMs: number): readonly PoolMember[];
|
|
13
|
-
/** Ranks `identities` from what the usage store recorded, as of `nowMs`. */
|
|
14
|
-
export declare function rankPoolFromStore(params: Readonly<{
|
|
15
|
-
fs: PoolPickFs;
|
|
16
|
-
paths: PoolPickPaths;
|
|
17
|
-
identities: readonly string[];
|
|
18
|
-
nowMs: number;
|
|
19
|
-
sticky?: StickyPick;
|
|
20
|
-
resuming: boolean;
|
|
21
|
-
}>): PoolRanking;
|
|
22
|
-
/** The identity last picked from a pool for `directory`, with a note when the picks file was unreadable (a hint lost, never a launch blocked). */
|
|
23
|
-
export declare function readStickyPick(fs: Pick<FarmFs, "readFileUtf8">, picksFile: string, directory: string): {
|
|
24
|
-
readonly sticky?: StickyPick;
|
|
25
|
-
readonly problem?: string;
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* Records that `identity` was picked for `directory` at `nowMs`, dropping entries older than the usage log's retention so the file stays bounded by what the rest of the store keeps. The write is a whole-file atomic replace; two launches finishing together can lose one entry, which only costs a cache hint.
|
|
29
|
-
*/
|
|
30
|
-
export declare function recordStickyPick(fs: Pick<FarmFs, "readFileUtf8" | "writeFilePrivate" | "mkdirPrivate">, picksFile: string, directory: string, identity: string, nowMs: number): void;
|
|
31
|
-
export {};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { QuotaWindow, UsageSnapshot } from "./schema";
|
|
2
|
-
/** A duration as its largest whole unit, such as `3h` or `2d`. */
|
|
3
|
-
export declare function formatAge(ms: number): string;
|
|
4
|
-
/** A quota window as it stands at a given instant. */
|
|
5
|
-
export interface EffectiveWindow {
|
|
6
|
-
/** True when the window's reset time has passed, so the recorded observation describes a window that no longer exists. */
|
|
7
|
-
readonly reset: boolean;
|
|
8
|
-
/** The fraction used: 0 for a reset window, otherwise what was last observed (a lower bound, since utilisation only rises within a window), or undefined when none was reported. */
|
|
9
|
-
readonly utilization?: number;
|
|
10
|
-
/** The reset instant, for a window that has not reset yet and reported one. */
|
|
11
|
-
readonly resetsAtMs?: number;
|
|
12
|
-
/** The window's own status, for a window that has not reset yet. */
|
|
13
|
-
readonly status?: string;
|
|
14
|
-
}
|
|
15
|
-
/** Reads a recorded window at `nowMs`: a window whose reset has passed is empty and carries no status, so every consumer agrees on what an old observation still means. */
|
|
16
|
-
export declare function effectiveWindow(window: Readonly<QuotaWindow>, nowMs: number): EffectiveWindow;
|
|
17
|
-
/**
|
|
18
|
-
* The launch-time warnings for an identity's quota, from the last rate-limit state the front door recorded for the provider this launch will use (`anthropic` for an OAuth launch). A window is reported when the API itself last marked it `allowed_warning` or `rejected`, and only while that window has not reset since: a reset time in the past means the observation describes a window that no longer exists. Each warning states how old the observation is, because the snapshot only changes when a request goes through the front door. Warnings never block a launch.
|
|
19
|
-
*/
|
|
20
|
-
export declare function quotaWarnings(snapshot: UsageSnapshot | undefined, provider: string, nowMs: number): string[];
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { type LimitClassification, type UnifiedRateLimit } from "./schema";
|
|
2
|
-
/** What a refused response's error body said, as far as classification needs: its machine-readable type and code, and its message, which is read here and never stored. */
|
|
3
|
-
export interface ErrorInfo {
|
|
4
|
-
readonly type?: string;
|
|
5
|
-
readonly code?: string;
|
|
6
|
-
readonly message?: string;
|
|
7
|
-
}
|
|
8
|
-
/** Keeps a response's rate-limit headers (see `isRateLimitHeader`), names lowercased; undefined when there are none. */
|
|
9
|
-
export declare function rateLimitHeadersOf(headers: Readonly<Record<string, string>>): Record<string, string> | undefined;
|
|
10
|
-
/**
|
|
11
|
-
* Parses Anthropic's unified subscription rate-limit headers, from `rateLimitHeadersOf`'s lowercased map. These were captured from a real `api.anthropic.com` response to an OAuth (subscription) session through the front door: `-status`, `-5h-status`, `-5h-utilization` (a fraction), `-5h-reset` (epoch seconds), the same three for `7d`, `-representative-claim`, `-reset`, `-overage-status`, `-overage-disabled-reason`, `-fallback` and `-fallback-percentage`. Undefined when the response carried none of them (an API-key or third-party provider response).
|
|
12
|
-
*/
|
|
13
|
-
export declare function parseUnifiedRateLimit(headers: Readonly<Record<string, string>>): UnifiedRateLimit | undefined;
|
|
14
|
-
/**
|
|
15
|
-
* Classifies a refused response as a short rate limit or an exhausted quota, the distinction the credential fallback acts on (a short limit retries the same credential once its wait has passed, an exhausted quota moves to the next one). Only a 429 or a 402 is classified; anything else is undefined.
|
|
16
|
-
*
|
|
17
|
-
* An exhausted quota is any of: a 402 (OpenRouter's "insufficient credits"); Anthropic's unified status `rejected` with no extra usage to fall back on (`overage-status` absent or `rejected`), the rule Claude Code itself applies before it waits for the plan's reset; an error type or code in `QUOTA_EXHAUSTED_ERRORS`; or an error message naming a usage limit. Every other 429 is a short rate limit. `resetAt` comes from the unified reset header, `retryAfterSeconds` from `retry-after`.
|
|
18
|
-
*/
|
|
19
|
-
export declare function classifyLimit(params: {
|
|
20
|
-
readonly status: number;
|
|
21
|
-
/** The response's rate-limit headers, as `rateLimitHeadersOf` returns them. */
|
|
22
|
-
readonly headers: Readonly<Record<string, string>>;
|
|
23
|
-
readonly error: ErrorInfo | undefined;
|
|
24
|
-
readonly nowMs: number;
|
|
25
|
-
}): LimitClassification | undefined;
|