claude-use 5.2.0 → 5.3.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 +52 -1
- package/dist/index.cjs +189 -2
- package/dist/index.mjs +184 -2
- package/dist/types/index.d.ts +5 -2
- 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 +259 -0
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -273458,7 +273458,7 @@ function resolveLaunchIdentity(paths, cwd) {
|
|
|
273458
273458
|
// package.json
|
|
273459
273459
|
var package_default = {
|
|
273460
273460
|
name: "claude-use",
|
|
273461
|
-
version: "5.
|
|
273461
|
+
version: "5.3.0",
|
|
273462
273462
|
description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
273463
273463
|
license: "Apache-2.0",
|
|
273464
273464
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -280549,6 +280549,44 @@ function spawnClaude(params) {
|
|
|
280549
280549
|
return params.proc.exit(exitCodeFor(result));
|
|
280550
280550
|
}
|
|
280551
280551
|
|
|
280552
|
+
// src/usage/preflight.ts
|
|
280553
|
+
var MS_PER_SECOND4 = 1e3;
|
|
280554
|
+
var SECONDS_PER_MINUTE = 60;
|
|
280555
|
+
var MINUTES_PER_HOUR = 60;
|
|
280556
|
+
var HOURS_PER_DAY = 24;
|
|
280557
|
+
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
280558
|
+
function formatAge(ms) {
|
|
280559
|
+
const minutes = Math.floor(ms / (MS_PER_SECOND4 * SECONDS_PER_MINUTE));
|
|
280560
|
+
if (minutes < MINUTES_PER_HOUR) {
|
|
280561
|
+
return `${String(Math.max(minutes, 0))}m`;
|
|
280562
|
+
}
|
|
280563
|
+
const hours = Math.floor(minutes / MINUTES_PER_HOUR);
|
|
280564
|
+
return hours < HOURS_PER_DAY ? `${String(hours)}h` : `${String(Math.floor(hours / HOURS_PER_DAY))}d`;
|
|
280565
|
+
}
|
|
280566
|
+
function quotaWarnings(snapshot, provider, nowMs) {
|
|
280567
|
+
const rateLimit = snapshot?.providers[provider]?.rateLimit;
|
|
280568
|
+
const unified = rateLimit?.unified;
|
|
280569
|
+
if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
|
|
280570
|
+
return [];
|
|
280571
|
+
}
|
|
280572
|
+
const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
|
|
280573
|
+
const windows = [
|
|
280574
|
+
{ name: "five-hour", window: unified.fiveHour },
|
|
280575
|
+
{ name: "seven-day", window: unified.sevenDay }
|
|
280576
|
+
];
|
|
280577
|
+
return windows.flatMap(({ name, window }) => {
|
|
280578
|
+
if (window?.status === void 0 || !WARNING_STATUSES.has(window.status)) {
|
|
280579
|
+
return [];
|
|
280580
|
+
}
|
|
280581
|
+
if (window.resetsAt !== void 0 && Date.parse(window.resetsAt) <= nowMs) {
|
|
280582
|
+
return [];
|
|
280583
|
+
}
|
|
280584
|
+
const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
|
|
280585
|
+
const verdict = window.status === "rejected" ? "exhausted" : "nearly used";
|
|
280586
|
+
return [`claude-use: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
|
|
280587
|
+
});
|
|
280588
|
+
}
|
|
280589
|
+
|
|
280552
280590
|
// src/launcher.ts
|
|
280553
280591
|
function resolveIdentityCredential(identity, credential, env, port) {
|
|
280554
280592
|
if (port === void 0) {
|
|
@@ -280645,6 +280683,19 @@ function runLauncher(params) {
|
|
|
280645
280683
|
for (const warning of provider?.ok === true ? provider.warnings : []) {
|
|
280646
280684
|
log.warn(warning);
|
|
280647
280685
|
}
|
|
280686
|
+
if (farm !== void 0 && farmIdentity !== void 0) {
|
|
280687
|
+
try {
|
|
280688
|
+
const snapshot = readUsageSnapshot(farm.fs, paths.usageSnapshotsDir, farmIdentity);
|
|
280689
|
+
for (const warning of quotaWarnings(snapshot, resolvedProvider?.name ?? ANTHROPIC_PROVIDER, farm.now())) {
|
|
280690
|
+
log.warn(warning);
|
|
280691
|
+
}
|
|
280692
|
+
} catch (error62) {
|
|
280693
|
+
if (!(error62 instanceof UsageSnapshotError)) {
|
|
280694
|
+
throw error62;
|
|
280695
|
+
}
|
|
280696
|
+
log.warn(`claude-use: ${error62.message}`);
|
|
280697
|
+
}
|
|
280698
|
+
}
|
|
280648
280699
|
const identityCredentialBlock = loadedIdentity?.config.credential;
|
|
280649
280700
|
const identityResolution = resolvedProvider === void 0 && farmIdentity !== void 0 && identityCredentialBlock !== void 0 ? resolveIdentityCredential(farmIdentity, identityCredentialBlock, env, params.credentials) : void 0;
|
|
280650
280701
|
if (identityResolution !== void 0 && !identityResolution.ok) {
|
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,8 @@ __export(index_exports, {
|
|
|
37
37
|
FrontDoorStartError: () => FrontDoorStartError,
|
|
38
38
|
HeadroomStartError: () => HeadroomStartError,
|
|
39
39
|
ROUTED_PATH_PREFIX: () => ROUTED_PATH_PREFIX,
|
|
40
|
+
UsageSnapshotError: () => UsageSnapshotError,
|
|
41
|
+
UsageSnapshotSchema: () => UsageSnapshotSchema,
|
|
40
42
|
buildEntryFacts: () => buildEntryFacts,
|
|
41
43
|
carryOver: () => carryOver,
|
|
42
44
|
createLeafCache: () => createLeafCache,
|
|
@@ -49,9 +51,11 @@ __export(index_exports, {
|
|
|
49
51
|
forwardableHeaders: () => forwardableHeaders,
|
|
50
52
|
generateCa: () => generateCa,
|
|
51
53
|
isInterceptedHost: () => isInterceptedHost,
|
|
54
|
+
listUsageSnapshots: () => listUsageSnapshots,
|
|
52
55
|
mintLeaf: () => mintLeaf,
|
|
53
56
|
parseConnectTarget: () => parseConnectTarget,
|
|
54
57
|
readFarmManifest: () => readFarmManifest,
|
|
58
|
+
readUsageSnapshot: () => readUsageSnapshot,
|
|
55
59
|
realConnectCertStore: () => realConnectCertStore,
|
|
56
60
|
realConnectEffects: () => realConnectEffects,
|
|
57
61
|
recoverFarm: () => recoverFarm,
|
|
@@ -62,6 +66,7 @@ __export(index_exports, {
|
|
|
62
66
|
runFrontDoorSupervisor: () => runFrontDoorSupervisor,
|
|
63
67
|
runSupervisor: () => runSupervisor,
|
|
64
68
|
servedByPipeline: () => servedByPipeline,
|
|
69
|
+
snapshotPath: () => snapshotPath,
|
|
65
70
|
startConnectServer: () => startConnectServer,
|
|
66
71
|
topLevelNames: () => topLevelNames
|
|
67
72
|
});
|
|
@@ -2346,8 +2351,8 @@ function parseConnectTarget(authority) {
|
|
|
2346
2351
|
function isInterceptedHost(host, interceptHost) {
|
|
2347
2352
|
return host === interceptHost;
|
|
2348
2353
|
}
|
|
2349
|
-
function servedByPipeline(
|
|
2350
|
-
return
|
|
2354
|
+
function servedByPipeline(path8) {
|
|
2355
|
+
return path8?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2351
2356
|
}
|
|
2352
2357
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2353
2358
|
"connection",
|
|
@@ -2952,6 +2957,183 @@ function ensureFrontDoor(params) {
|
|
|
2952
2957
|
}
|
|
2953
2958
|
}
|
|
2954
2959
|
|
|
2960
|
+
// src/usage/read.ts
|
|
2961
|
+
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
2962
|
+
|
|
2963
|
+
// src/usage/schema.ts
|
|
2964
|
+
var import_zod5 = require("zod");
|
|
2965
|
+
var USAGE_SCHEMA_VERSION = 1;
|
|
2966
|
+
var VersionSchema = import_zod5.z.literal(USAGE_SCHEMA_VERSION);
|
|
2967
|
+
var InstantSchema = import_zod5.z.iso.datetime();
|
|
2968
|
+
var TokenCountSchema = import_zod5.z.number().int().nonnegative();
|
|
2969
|
+
var TokenUsageSchema = import_zod5.z.strictObject({
|
|
2970
|
+
inputTokens: TokenCountSchema.optional(),
|
|
2971
|
+
outputTokens: TokenCountSchema.optional(),
|
|
2972
|
+
cacheCreationInputTokens: TokenCountSchema.optional(),
|
|
2973
|
+
cacheReadInputTokens: TokenCountSchema.optional()
|
|
2974
|
+
});
|
|
2975
|
+
var LIMIT_KINDS = ["rate-limited", "quota-exhausted"];
|
|
2976
|
+
var LimitClassificationSchema = import_zod5.z.strictObject({
|
|
2977
|
+
kind: import_zod5.z.enum(LIMIT_KINDS),
|
|
2978
|
+
/** Seconds to wait before retrying, from the response's `retry-after`. */
|
|
2979
|
+
retryAfterSeconds: import_zod5.z.number().nonnegative().optional(),
|
|
2980
|
+
/** When the limiting window resets, from the upstream's own reset header. */
|
|
2981
|
+
resetAt: InstantSchema.optional(),
|
|
2982
|
+
/** The window the upstream named as the binding one (`anthropic-ratelimit-unified-representative-claim`), such as `five_hour` or `seven_day`. */
|
|
2983
|
+
window: import_zod5.z.string().optional(),
|
|
2984
|
+
/** The signals the classification rests on, each `<name>=<value>` from a response header or the error body's `type` or `code`. Never the error message. */
|
|
2985
|
+
evidence: import_zod5.z.array(import_zod5.z.string())
|
|
2986
|
+
});
|
|
2987
|
+
var UsageRecordSchema = import_zod5.z.strictObject({
|
|
2988
|
+
schemaVersion: VersionSchema,
|
|
2989
|
+
/** When the front door received the request. */
|
|
2990
|
+
at: InstantSchema,
|
|
2991
|
+
/** The launching identity, when the launch resolved one. */
|
|
2992
|
+
identity: import_zod5.z.string().optional(),
|
|
2993
|
+
/** `anthropic` for an OAuth session's request to Claude Code's own API, otherwise the provider's name. */
|
|
2994
|
+
provider: import_zod5.z.string(),
|
|
2995
|
+
/** The launch's session id, generated per launch. */
|
|
2996
|
+
sessionId: import_zod5.z.string().optional(),
|
|
2997
|
+
/** The project the launch ran in (its git repository root), when the launch sent one: headroom launches do. */
|
|
2998
|
+
project: import_zod5.z.string().optional(),
|
|
2999
|
+
/** The front-door route that served the request. */
|
|
3000
|
+
route: import_zod5.z.string(),
|
|
3001
|
+
method: import_zod5.z.string(),
|
|
3002
|
+
/** The API path as the upstream sees it (`/v1/messages`), without the provider prefix or a query string. */
|
|
3003
|
+
endpoint: import_zod5.z.string(),
|
|
3004
|
+
/** The model the response named. */
|
|
3005
|
+
model: import_zod5.z.string().optional(),
|
|
3006
|
+
status: import_zod5.z.number().int(),
|
|
3007
|
+
/** Milliseconds from the request's arrival to its response head. */
|
|
3008
|
+
latencyMs: import_zod5.z.number().int().nonnegative(),
|
|
3009
|
+
/** Milliseconds from the request's arrival to the end of its response body. */
|
|
3010
|
+
durationMs: import_zod5.z.number().int().nonnegative(),
|
|
3011
|
+
/** Whether the client received the whole body. */
|
|
3012
|
+
outcome: import_zod5.z.enum(["completed", "aborted"]),
|
|
3013
|
+
usage: TokenUsageSchema.optional(),
|
|
3014
|
+
/** The response's quota and rate-limit headers, by name (see `isRateLimitHeader`). */
|
|
3015
|
+
rateLimitHeaders: import_zod5.z.record(import_zod5.z.string(), import_zod5.z.string()).optional(),
|
|
3016
|
+
limit: LimitClassificationSchema.optional(),
|
|
3017
|
+
/** The upstream's request id (`request-id`), for matching a record to the provider's own logs. */
|
|
3018
|
+
requestId: import_zod5.z.string().optional()
|
|
3019
|
+
});
|
|
3020
|
+
var QuotaWindowSchema = import_zod5.z.strictObject({
|
|
3021
|
+
/** The fraction of the window used, as the upstream reports it (0 to 1). */
|
|
3022
|
+
utilization: import_zod5.z.number().nonnegative().optional(),
|
|
3023
|
+
resetsAt: InstantSchema.optional(),
|
|
3024
|
+
/** The window's own status (`allowed`, `allowed_warning`, `rejected`). */
|
|
3025
|
+
status: import_zod5.z.string().optional()
|
|
3026
|
+
});
|
|
3027
|
+
var UnifiedRateLimitSchema = import_zod5.z.strictObject({
|
|
3028
|
+
/** The overall status: `allowed`, `allowed_warning` or `rejected`. */
|
|
3029
|
+
status: import_zod5.z.string().optional(),
|
|
3030
|
+
fiveHour: QuotaWindowSchema.optional(),
|
|
3031
|
+
sevenDay: QuotaWindowSchema.optional(),
|
|
3032
|
+
/** The window that currently binds. */
|
|
3033
|
+
representativeClaim: import_zod5.z.string().optional(),
|
|
3034
|
+
/** When the binding window resets. */
|
|
3035
|
+
resetAt: InstantSchema.optional(),
|
|
3036
|
+
/** Whether usage beyond the plan (extra usage) is available: `allowed` or `rejected`. */
|
|
3037
|
+
overageStatus: import_zod5.z.string().optional()
|
|
3038
|
+
});
|
|
3039
|
+
var RateLimitStateSchema = import_zod5.z.strictObject({
|
|
3040
|
+
observedAt: InstantSchema,
|
|
3041
|
+
/** Every quota and rate-limit header on that response, by name. */
|
|
3042
|
+
headers: import_zod5.z.record(import_zod5.z.string(), import_zod5.z.string()),
|
|
3043
|
+
unified: UnifiedRateLimitSchema.optional()
|
|
3044
|
+
});
|
|
3045
|
+
var LimitEventSchema = LimitClassificationSchema.extend({
|
|
3046
|
+
observedAt: InstantSchema,
|
|
3047
|
+
status: import_zod5.z.number().int()
|
|
3048
|
+
});
|
|
3049
|
+
var ProviderUsageStateSchema = import_zod5.z.strictObject({
|
|
3050
|
+
lastRequestAt: InstantSchema,
|
|
3051
|
+
lastStatus: import_zod5.z.number().int(),
|
|
3052
|
+
lastModel: import_zod5.z.string().optional(),
|
|
3053
|
+
/** The latest rate-limit state, from the last response that carried any rate-limit header. */
|
|
3054
|
+
rateLimit: RateLimitStateSchema.optional(),
|
|
3055
|
+
/** The latest classified refusal. It stays after later successes, so a reader compares its `resetAt` (or `observedAt` plus `retryAfterSeconds`) with the current time. */
|
|
3056
|
+
lastLimit: LimitEventSchema.optional()
|
|
3057
|
+
});
|
|
3058
|
+
var AccountMetadataSchema = import_zod5.z.strictObject({
|
|
3059
|
+
accountUuid: import_zod5.z.string().optional(),
|
|
3060
|
+
emailAddress: import_zod5.z.string().optional(),
|
|
3061
|
+
displayName: import_zod5.z.string().optional(),
|
|
3062
|
+
organizationUuid: import_zod5.z.string().optional(),
|
|
3063
|
+
organizationName: import_zod5.z.string().optional(),
|
|
3064
|
+
organizationType: import_zod5.z.string().optional(),
|
|
3065
|
+
organizationRole: import_zod5.z.string().optional(),
|
|
3066
|
+
workspaceRole: import_zod5.z.string().optional(),
|
|
3067
|
+
billingType: import_zod5.z.string().optional(),
|
|
3068
|
+
seatTier: import_zod5.z.string().optional(),
|
|
3069
|
+
organizationRateLimitTier: import_zod5.z.string().optional(),
|
|
3070
|
+
userRateLimitTier: import_zod5.z.string().optional(),
|
|
3071
|
+
hasExtraUsageEnabled: import_zod5.z.boolean().optional(),
|
|
3072
|
+
subscriptionCreatedAt: import_zod5.z.string().optional(),
|
|
3073
|
+
accountCreatedAt: import_zod5.z.string().optional()
|
|
3074
|
+
});
|
|
3075
|
+
var UsageSnapshotSchema = import_zod5.z.strictObject({
|
|
3076
|
+
schemaVersion: VersionSchema,
|
|
3077
|
+
identity: import_zod5.z.string(),
|
|
3078
|
+
updatedAt: InstantSchema,
|
|
3079
|
+
/** The identity's account metadata as of `updatedAt`. */
|
|
3080
|
+
account: AccountMetadataSchema.optional(),
|
|
3081
|
+
/** Latest state per provider, keyed by provider name (`anthropic` for OAuth sessions). */
|
|
3082
|
+
providers: import_zod5.z.record(import_zod5.z.string(), ProviderUsageStateSchema)
|
|
3083
|
+
});
|
|
3084
|
+
|
|
3085
|
+
// src/usage/account.ts
|
|
3086
|
+
function isIdentityName(name) {
|
|
3087
|
+
return IdentitySchema.shape.name.safeParse(name).success;
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
// src/usage/read.ts
|
|
3091
|
+
var UsageSnapshotError = class extends Error {
|
|
3092
|
+
constructor(file, detail) {
|
|
3093
|
+
super(`${file} is not a usage snapshot this claude-use can read (schema version ${String(USAGE_SCHEMA_VERSION)}): ${detail}`);
|
|
3094
|
+
this.file = file;
|
|
3095
|
+
this.name = "UsageSnapshotError";
|
|
3096
|
+
}
|
|
3097
|
+
file;
|
|
3098
|
+
};
|
|
3099
|
+
function snapshotPath(snapshotsDir, identity) {
|
|
3100
|
+
if (!isIdentityName(identity)) {
|
|
3101
|
+
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3102
|
+
}
|
|
3103
|
+
return import_node_path7.default.join(snapshotsDir, `${identity}.json`);
|
|
3104
|
+
}
|
|
3105
|
+
function readUsageSnapshot(fs2, snapshotsDir, identity) {
|
|
3106
|
+
const file = snapshotPath(snapshotsDir, identity);
|
|
3107
|
+
const raw = fs2.readFileUtf8(file);
|
|
3108
|
+
if (raw === void 0) {
|
|
3109
|
+
return void 0;
|
|
3110
|
+
}
|
|
3111
|
+
let parsed;
|
|
3112
|
+
try {
|
|
3113
|
+
parsed = JSON.parse(raw);
|
|
3114
|
+
} catch {
|
|
3115
|
+
throw new UsageSnapshotError(file, "not valid JSON");
|
|
3116
|
+
}
|
|
3117
|
+
const snapshot = UsageSnapshotSchema.safeParse(parsed);
|
|
3118
|
+
if (!snapshot.success) {
|
|
3119
|
+
throw new UsageSnapshotError(file, snapshot.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; "));
|
|
3120
|
+
}
|
|
3121
|
+
return snapshot.data;
|
|
3122
|
+
}
|
|
3123
|
+
function listUsageSnapshots(fs2, snapshotsDir) {
|
|
3124
|
+
return fs2.readdir(snapshotsDir).flatMap((name) => {
|
|
3125
|
+
if (!name.endsWith(".json")) {
|
|
3126
|
+
return [];
|
|
3127
|
+
}
|
|
3128
|
+
const identity = name.slice(0, -".json".length);
|
|
3129
|
+
if (!isIdentityName(identity)) {
|
|
3130
|
+
return [];
|
|
3131
|
+
}
|
|
3132
|
+
const snapshot = readUsageSnapshot(fs2, snapshotsDir, identity);
|
|
3133
|
+
return snapshot === void 0 ? [] : [snapshot];
|
|
3134
|
+
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
3135
|
+
}
|
|
3136
|
+
|
|
2955
3137
|
// src/headroom/ensure.ts
|
|
2956
3138
|
var HEADROOM_START_TIMEOUT_MS = 12e4;
|
|
2957
3139
|
var HEADROOM_LAUNCHER_POLL_MS = 250;
|
|
@@ -3326,6 +3508,8 @@ function evaluateAmbientCredentialGuard(params) {
|
|
|
3326
3508
|
FrontDoorStartError,
|
|
3327
3509
|
HeadroomStartError,
|
|
3328
3510
|
ROUTED_PATH_PREFIX,
|
|
3511
|
+
UsageSnapshotError,
|
|
3512
|
+
UsageSnapshotSchema,
|
|
3329
3513
|
buildEntryFacts,
|
|
3330
3514
|
carryOver,
|
|
3331
3515
|
createLeafCache,
|
|
@@ -3338,9 +3522,11 @@ function evaluateAmbientCredentialGuard(params) {
|
|
|
3338
3522
|
forwardableHeaders,
|
|
3339
3523
|
generateCa,
|
|
3340
3524
|
isInterceptedHost,
|
|
3525
|
+
listUsageSnapshots,
|
|
3341
3526
|
mintLeaf,
|
|
3342
3527
|
parseConnectTarget,
|
|
3343
3528
|
readFarmManifest,
|
|
3529
|
+
readUsageSnapshot,
|
|
3344
3530
|
realConnectCertStore,
|
|
3345
3531
|
realConnectEffects,
|
|
3346
3532
|
recoverFarm,
|
|
@@ -3351,6 +3537,7 @@ function evaluateAmbientCredentialGuard(params) {
|
|
|
3351
3537
|
runFrontDoorSupervisor,
|
|
3352
3538
|
runSupervisor,
|
|
3353
3539
|
servedByPipeline,
|
|
3540
|
+
snapshotPath,
|
|
3354
3541
|
startConnectServer,
|
|
3355
3542
|
topLevelNames
|
|
3356
3543
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -2277,8 +2277,8 @@ function parseConnectTarget(authority) {
|
|
|
2277
2277
|
function isInterceptedHost(host, interceptHost) {
|
|
2278
2278
|
return host === interceptHost;
|
|
2279
2279
|
}
|
|
2280
|
-
function servedByPipeline(
|
|
2281
|
-
return
|
|
2280
|
+
function servedByPipeline(path8) {
|
|
2281
|
+
return path8?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2282
2282
|
}
|
|
2283
2283
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2284
2284
|
"connection",
|
|
@@ -2883,6 +2883,183 @@ function ensureFrontDoor(params) {
|
|
|
2883
2883
|
}
|
|
2884
2884
|
}
|
|
2885
2885
|
|
|
2886
|
+
// src/usage/read.ts
|
|
2887
|
+
import path7 from "node:path";
|
|
2888
|
+
|
|
2889
|
+
// src/usage/schema.ts
|
|
2890
|
+
import { z as z5 } from "zod";
|
|
2891
|
+
var USAGE_SCHEMA_VERSION = 1;
|
|
2892
|
+
var VersionSchema = z5.literal(USAGE_SCHEMA_VERSION);
|
|
2893
|
+
var InstantSchema = z5.iso.datetime();
|
|
2894
|
+
var TokenCountSchema = z5.number().int().nonnegative();
|
|
2895
|
+
var TokenUsageSchema = z5.strictObject({
|
|
2896
|
+
inputTokens: TokenCountSchema.optional(),
|
|
2897
|
+
outputTokens: TokenCountSchema.optional(),
|
|
2898
|
+
cacheCreationInputTokens: TokenCountSchema.optional(),
|
|
2899
|
+
cacheReadInputTokens: TokenCountSchema.optional()
|
|
2900
|
+
});
|
|
2901
|
+
var LIMIT_KINDS = ["rate-limited", "quota-exhausted"];
|
|
2902
|
+
var LimitClassificationSchema = z5.strictObject({
|
|
2903
|
+
kind: z5.enum(LIMIT_KINDS),
|
|
2904
|
+
/** Seconds to wait before retrying, from the response's `retry-after`. */
|
|
2905
|
+
retryAfterSeconds: z5.number().nonnegative().optional(),
|
|
2906
|
+
/** When the limiting window resets, from the upstream's own reset header. */
|
|
2907
|
+
resetAt: InstantSchema.optional(),
|
|
2908
|
+
/** The window the upstream named as the binding one (`anthropic-ratelimit-unified-representative-claim`), such as `five_hour` or `seven_day`. */
|
|
2909
|
+
window: z5.string().optional(),
|
|
2910
|
+
/** The signals the classification rests on, each `<name>=<value>` from a response header or the error body's `type` or `code`. Never the error message. */
|
|
2911
|
+
evidence: z5.array(z5.string())
|
|
2912
|
+
});
|
|
2913
|
+
var UsageRecordSchema = z5.strictObject({
|
|
2914
|
+
schemaVersion: VersionSchema,
|
|
2915
|
+
/** When the front door received the request. */
|
|
2916
|
+
at: InstantSchema,
|
|
2917
|
+
/** The launching identity, when the launch resolved one. */
|
|
2918
|
+
identity: z5.string().optional(),
|
|
2919
|
+
/** `anthropic` for an OAuth session's request to Claude Code's own API, otherwise the provider's name. */
|
|
2920
|
+
provider: z5.string(),
|
|
2921
|
+
/** The launch's session id, generated per launch. */
|
|
2922
|
+
sessionId: z5.string().optional(),
|
|
2923
|
+
/** The project the launch ran in (its git repository root), when the launch sent one: headroom launches do. */
|
|
2924
|
+
project: z5.string().optional(),
|
|
2925
|
+
/** The front-door route that served the request. */
|
|
2926
|
+
route: z5.string(),
|
|
2927
|
+
method: z5.string(),
|
|
2928
|
+
/** The API path as the upstream sees it (`/v1/messages`), without the provider prefix or a query string. */
|
|
2929
|
+
endpoint: z5.string(),
|
|
2930
|
+
/** The model the response named. */
|
|
2931
|
+
model: z5.string().optional(),
|
|
2932
|
+
status: z5.number().int(),
|
|
2933
|
+
/** Milliseconds from the request's arrival to its response head. */
|
|
2934
|
+
latencyMs: z5.number().int().nonnegative(),
|
|
2935
|
+
/** Milliseconds from the request's arrival to the end of its response body. */
|
|
2936
|
+
durationMs: z5.number().int().nonnegative(),
|
|
2937
|
+
/** Whether the client received the whole body. */
|
|
2938
|
+
outcome: z5.enum(["completed", "aborted"]),
|
|
2939
|
+
usage: TokenUsageSchema.optional(),
|
|
2940
|
+
/** The response's quota and rate-limit headers, by name (see `isRateLimitHeader`). */
|
|
2941
|
+
rateLimitHeaders: z5.record(z5.string(), z5.string()).optional(),
|
|
2942
|
+
limit: LimitClassificationSchema.optional(),
|
|
2943
|
+
/** The upstream's request id (`request-id`), for matching a record to the provider's own logs. */
|
|
2944
|
+
requestId: z5.string().optional()
|
|
2945
|
+
});
|
|
2946
|
+
var QuotaWindowSchema = z5.strictObject({
|
|
2947
|
+
/** The fraction of the window used, as the upstream reports it (0 to 1). */
|
|
2948
|
+
utilization: z5.number().nonnegative().optional(),
|
|
2949
|
+
resetsAt: InstantSchema.optional(),
|
|
2950
|
+
/** The window's own status (`allowed`, `allowed_warning`, `rejected`). */
|
|
2951
|
+
status: z5.string().optional()
|
|
2952
|
+
});
|
|
2953
|
+
var UnifiedRateLimitSchema = z5.strictObject({
|
|
2954
|
+
/** The overall status: `allowed`, `allowed_warning` or `rejected`. */
|
|
2955
|
+
status: z5.string().optional(),
|
|
2956
|
+
fiveHour: QuotaWindowSchema.optional(),
|
|
2957
|
+
sevenDay: QuotaWindowSchema.optional(),
|
|
2958
|
+
/** The window that currently binds. */
|
|
2959
|
+
representativeClaim: z5.string().optional(),
|
|
2960
|
+
/** When the binding window resets. */
|
|
2961
|
+
resetAt: InstantSchema.optional(),
|
|
2962
|
+
/** Whether usage beyond the plan (extra usage) is available: `allowed` or `rejected`. */
|
|
2963
|
+
overageStatus: z5.string().optional()
|
|
2964
|
+
});
|
|
2965
|
+
var RateLimitStateSchema = z5.strictObject({
|
|
2966
|
+
observedAt: InstantSchema,
|
|
2967
|
+
/** Every quota and rate-limit header on that response, by name. */
|
|
2968
|
+
headers: z5.record(z5.string(), z5.string()),
|
|
2969
|
+
unified: UnifiedRateLimitSchema.optional()
|
|
2970
|
+
});
|
|
2971
|
+
var LimitEventSchema = LimitClassificationSchema.extend({
|
|
2972
|
+
observedAt: InstantSchema,
|
|
2973
|
+
status: z5.number().int()
|
|
2974
|
+
});
|
|
2975
|
+
var ProviderUsageStateSchema = z5.strictObject({
|
|
2976
|
+
lastRequestAt: InstantSchema,
|
|
2977
|
+
lastStatus: z5.number().int(),
|
|
2978
|
+
lastModel: z5.string().optional(),
|
|
2979
|
+
/** The latest rate-limit state, from the last response that carried any rate-limit header. */
|
|
2980
|
+
rateLimit: RateLimitStateSchema.optional(),
|
|
2981
|
+
/** The latest classified refusal. It stays after later successes, so a reader compares its `resetAt` (or `observedAt` plus `retryAfterSeconds`) with the current time. */
|
|
2982
|
+
lastLimit: LimitEventSchema.optional()
|
|
2983
|
+
});
|
|
2984
|
+
var AccountMetadataSchema = z5.strictObject({
|
|
2985
|
+
accountUuid: z5.string().optional(),
|
|
2986
|
+
emailAddress: z5.string().optional(),
|
|
2987
|
+
displayName: z5.string().optional(),
|
|
2988
|
+
organizationUuid: z5.string().optional(),
|
|
2989
|
+
organizationName: z5.string().optional(),
|
|
2990
|
+
organizationType: z5.string().optional(),
|
|
2991
|
+
organizationRole: z5.string().optional(),
|
|
2992
|
+
workspaceRole: z5.string().optional(),
|
|
2993
|
+
billingType: z5.string().optional(),
|
|
2994
|
+
seatTier: z5.string().optional(),
|
|
2995
|
+
organizationRateLimitTier: z5.string().optional(),
|
|
2996
|
+
userRateLimitTier: z5.string().optional(),
|
|
2997
|
+
hasExtraUsageEnabled: z5.boolean().optional(),
|
|
2998
|
+
subscriptionCreatedAt: z5.string().optional(),
|
|
2999
|
+
accountCreatedAt: z5.string().optional()
|
|
3000
|
+
});
|
|
3001
|
+
var UsageSnapshotSchema = z5.strictObject({
|
|
3002
|
+
schemaVersion: VersionSchema,
|
|
3003
|
+
identity: z5.string(),
|
|
3004
|
+
updatedAt: InstantSchema,
|
|
3005
|
+
/** The identity's account metadata as of `updatedAt`. */
|
|
3006
|
+
account: AccountMetadataSchema.optional(),
|
|
3007
|
+
/** Latest state per provider, keyed by provider name (`anthropic` for OAuth sessions). */
|
|
3008
|
+
providers: z5.record(z5.string(), ProviderUsageStateSchema)
|
|
3009
|
+
});
|
|
3010
|
+
|
|
3011
|
+
// src/usage/account.ts
|
|
3012
|
+
function isIdentityName(name) {
|
|
3013
|
+
return IdentitySchema.shape.name.safeParse(name).success;
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
// src/usage/read.ts
|
|
3017
|
+
var UsageSnapshotError = class extends Error {
|
|
3018
|
+
constructor(file, detail) {
|
|
3019
|
+
super(`${file} is not a usage snapshot this claude-use can read (schema version ${String(USAGE_SCHEMA_VERSION)}): ${detail}`);
|
|
3020
|
+
this.file = file;
|
|
3021
|
+
this.name = "UsageSnapshotError";
|
|
3022
|
+
}
|
|
3023
|
+
file;
|
|
3024
|
+
};
|
|
3025
|
+
function snapshotPath(snapshotsDir, identity) {
|
|
3026
|
+
if (!isIdentityName(identity)) {
|
|
3027
|
+
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3028
|
+
}
|
|
3029
|
+
return path7.join(snapshotsDir, `${identity}.json`);
|
|
3030
|
+
}
|
|
3031
|
+
function readUsageSnapshot(fs2, snapshotsDir, identity) {
|
|
3032
|
+
const file = snapshotPath(snapshotsDir, identity);
|
|
3033
|
+
const raw = fs2.readFileUtf8(file);
|
|
3034
|
+
if (raw === void 0) {
|
|
3035
|
+
return void 0;
|
|
3036
|
+
}
|
|
3037
|
+
let parsed;
|
|
3038
|
+
try {
|
|
3039
|
+
parsed = JSON.parse(raw);
|
|
3040
|
+
} catch {
|
|
3041
|
+
throw new UsageSnapshotError(file, "not valid JSON");
|
|
3042
|
+
}
|
|
3043
|
+
const snapshot = UsageSnapshotSchema.safeParse(parsed);
|
|
3044
|
+
if (!snapshot.success) {
|
|
3045
|
+
throw new UsageSnapshotError(file, snapshot.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; "));
|
|
3046
|
+
}
|
|
3047
|
+
return snapshot.data;
|
|
3048
|
+
}
|
|
3049
|
+
function listUsageSnapshots(fs2, snapshotsDir) {
|
|
3050
|
+
return fs2.readdir(snapshotsDir).flatMap((name) => {
|
|
3051
|
+
if (!name.endsWith(".json")) {
|
|
3052
|
+
return [];
|
|
3053
|
+
}
|
|
3054
|
+
const identity = name.slice(0, -".json".length);
|
|
3055
|
+
if (!isIdentityName(identity)) {
|
|
3056
|
+
return [];
|
|
3057
|
+
}
|
|
3058
|
+
const snapshot = readUsageSnapshot(fs2, snapshotsDir, identity);
|
|
3059
|
+
return snapshot === void 0 ? [] : [snapshot];
|
|
3060
|
+
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
3061
|
+
}
|
|
3062
|
+
|
|
2886
3063
|
// src/headroom/ensure.ts
|
|
2887
3064
|
var HEADROOM_START_TIMEOUT_MS = 12e4;
|
|
2888
3065
|
var HEADROOM_LAUNCHER_POLL_MS = 250;
|
|
@@ -3256,6 +3433,8 @@ export {
|
|
|
3256
3433
|
FrontDoorStartError,
|
|
3257
3434
|
HeadroomStartError,
|
|
3258
3435
|
ROUTED_PATH_PREFIX,
|
|
3436
|
+
UsageSnapshotError,
|
|
3437
|
+
UsageSnapshotSchema,
|
|
3259
3438
|
buildEntryFacts,
|
|
3260
3439
|
carryOver,
|
|
3261
3440
|
createLeafCache,
|
|
@@ -3268,9 +3447,11 @@ export {
|
|
|
3268
3447
|
forwardableHeaders,
|
|
3269
3448
|
generateCa,
|
|
3270
3449
|
isInterceptedHost,
|
|
3450
|
+
listUsageSnapshots,
|
|
3271
3451
|
mintLeaf,
|
|
3272
3452
|
parseConnectTarget,
|
|
3273
3453
|
readFarmManifest,
|
|
3454
|
+
readUsageSnapshot,
|
|
3274
3455
|
realConnectCertStore,
|
|
3275
3456
|
realConnectEffects,
|
|
3276
3457
|
recoverFarm,
|
|
@@ -3281,6 +3462,7 @@ export {
|
|
|
3281
3462
|
runFrontDoorSupervisor,
|
|
3282
3463
|
runSupervisor,
|
|
3283
3464
|
servedByPipeline,
|
|
3465
|
+
snapshotPath,
|
|
3284
3466
|
startConnectServer,
|
|
3285
3467
|
topLevelNames
|
|
3286
3468
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The library surface of claude-use: the pure, port-injected parts other tools can call in-process instead of shelling out to the CLI.
|
|
2
|
+
* The library surface of claude-use: the pure, port-injected parts other tools can call in-process instead of shelling out to the CLI. Four groups, none of which imports interactive prompting or argument parsing:
|
|
3
3
|
*
|
|
4
4
|
* - identity and configuration-profile resolution and farm sync (`resolveDecisions`, `resyncFarm`, `buildEntryFacts`, ...), pure over facts and ports passed in;
|
|
5
5
|
* - routing: the front door's Remote-Control-preserving, capability-authenticated CONNECT surface and its certificate authority, the front-door supervisor and ensure lifecycle, and the headroom supervisor and ensure lifecycle, each pure over its injected ports;
|
|
6
|
-
* - the ambient-credential guard
|
|
6
|
+
* - the ambient-credential guard;
|
|
7
|
+
* - the per-identity usage snapshot a statusline or launcher reads: its schema and the reader over an injected filesystem.
|
|
7
8
|
*
|
|
8
9
|
* Everything the CLI alone needs (commander wiring, prompts, `doctor`, `check`) is deliberately not exported here.
|
|
9
10
|
*/
|
|
@@ -13,6 +14,8 @@ export { buildEntryFacts, carryOver, FARM_MANIFEST_FILENAME, readFarmManifest, r
|
|
|
13
14
|
export { CONNECT_INTERCEPT_HOST, CONNECT_LIMITS, createLeafCache, ensureCa, forwardableHeaders, generateCa, isInterceptedHost, mintLeaf, parseConnectTarget, realConnectCertStore, realConnectEffects, ROUTED_PATH_PREFIX, servedByPipeline, startConnectServer, type CaMaterial, type ConnectCertStore, type ConnectEffects, type ConnectLimits, type ConnectServerConfig, type ConnectServerHandle, type ConnectTarget, type LeafCert, } from "./frontdoor/connect";
|
|
14
15
|
export { ensureFrontDoor, FrontDoorStartError, type EnsureFrontDoorPorts } from "./frontdoor/ensure";
|
|
15
16
|
export { runFrontDoorSupervisor, type FrontDoorSupervisorPorts, type RunFrontDoorSupervisorOptions } from "./frontdoor/supervisor";
|
|
17
|
+
export { listUsageSnapshots, readUsageSnapshot, snapshotPath, UsageSnapshotError, type UsageReadFs } from "./usage/read";
|
|
18
|
+
export { UsageSnapshotSchema, type UsageSnapshot } from "./usage/schema";
|
|
16
19
|
export { ensureHeadroom, HeadroomStartError, type EnsureHeadroomPorts } from "./headroom/ensure";
|
|
17
20
|
export { resolveSupervisorConfig, runSupervisor, type HeadroomSupervisorConfig, type RunSupervisorOptions, type SupervisorPorts } from "./headroom/supervisor";
|
|
18
21
|
export { AMBIENT_CREDENTIAL_VARS, detectAmbientCredential, evaluateAmbientCredentialGuard, formatAmbientCredentialGuardMessage, type AmbientCredentialDetection, type AmbientCredentialGuardResult, type AmbientCredentialVar, type EvaluateAmbientCredentialGuardParams, type InjectedCredential, } from "./launcher/guard";
|
|
@@ -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 claude-use 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,259 @@
|
|
|
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 provider's latest state under one identity. */
|
|
133
|
+
declare const ProviderUsageStateSchema: z.ZodObject<{
|
|
134
|
+
lastRequestAt: z.ZodISODateTime;
|
|
135
|
+
lastStatus: z.ZodNumber;
|
|
136
|
+
lastModel: z.ZodOptional<z.ZodString>;
|
|
137
|
+
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
138
|
+
observedAt: z.ZodISODateTime;
|
|
139
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
140
|
+
unified: z.ZodOptional<z.ZodObject<{
|
|
141
|
+
status: z.ZodOptional<z.ZodString>;
|
|
142
|
+
fiveHour: z.ZodOptional<z.ZodObject<{
|
|
143
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
144
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
145
|
+
status: z.ZodOptional<z.ZodString>;
|
|
146
|
+
}, z.core.$strict>>;
|
|
147
|
+
sevenDay: z.ZodOptional<z.ZodObject<{
|
|
148
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
149
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
150
|
+
status: z.ZodOptional<z.ZodString>;
|
|
151
|
+
}, z.core.$strict>>;
|
|
152
|
+
representativeClaim: z.ZodOptional<z.ZodString>;
|
|
153
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
154
|
+
overageStatus: z.ZodOptional<z.ZodString>;
|
|
155
|
+
}, z.core.$strict>>;
|
|
156
|
+
}, z.core.$strict>>;
|
|
157
|
+
lastLimit: z.ZodOptional<z.ZodObject<{
|
|
158
|
+
kind: z.ZodEnum<{
|
|
159
|
+
"rate-limited": "rate-limited";
|
|
160
|
+
"quota-exhausted": "quota-exhausted";
|
|
161
|
+
}>;
|
|
162
|
+
retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
|
|
163
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
164
|
+
window: z.ZodOptional<z.ZodString>;
|
|
165
|
+
evidence: z.ZodArray<z.ZodString>;
|
|
166
|
+
observedAt: z.ZodISODateTime;
|
|
167
|
+
status: z.ZodNumber;
|
|
168
|
+
}, z.core.$strict>>;
|
|
169
|
+
}, z.core.$strict>;
|
|
170
|
+
export type ProviderUsageState = z.infer<typeof ProviderUsageStateSchema>;
|
|
171
|
+
/**
|
|
172
|
+
* 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).
|
|
173
|
+
*/
|
|
174
|
+
export declare const AccountMetadataSchema: z.ZodObject<{
|
|
175
|
+
accountUuid: z.ZodOptional<z.ZodString>;
|
|
176
|
+
emailAddress: z.ZodOptional<z.ZodString>;
|
|
177
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
178
|
+
organizationUuid: z.ZodOptional<z.ZodString>;
|
|
179
|
+
organizationName: z.ZodOptional<z.ZodString>;
|
|
180
|
+
organizationType: z.ZodOptional<z.ZodString>;
|
|
181
|
+
organizationRole: z.ZodOptional<z.ZodString>;
|
|
182
|
+
workspaceRole: z.ZodOptional<z.ZodString>;
|
|
183
|
+
billingType: z.ZodOptional<z.ZodString>;
|
|
184
|
+
seatTier: z.ZodOptional<z.ZodString>;
|
|
185
|
+
organizationRateLimitTier: z.ZodOptional<z.ZodString>;
|
|
186
|
+
userRateLimitTier: z.ZodOptional<z.ZodString>;
|
|
187
|
+
hasExtraUsageEnabled: z.ZodOptional<z.ZodBoolean>;
|
|
188
|
+
subscriptionCreatedAt: z.ZodOptional<z.ZodString>;
|
|
189
|
+
accountCreatedAt: z.ZodOptional<z.ZodString>;
|
|
190
|
+
}, z.core.$strict>;
|
|
191
|
+
export type AccountMetadata = z.infer<typeof AccountMetadataSchema>;
|
|
192
|
+
/**
|
|
193
|
+
* The latest state for one identity, rewritten atomically after each recorded response: the snapshot file other tools read (`~/.claude-use/usage/snapshots/<identity>.json`) without shelling out to `claude-use`.
|
|
194
|
+
*/
|
|
195
|
+
export declare const UsageSnapshotSchema: z.ZodObject<{
|
|
196
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
197
|
+
identity: z.ZodString;
|
|
198
|
+
updatedAt: z.ZodISODateTime;
|
|
199
|
+
account: z.ZodOptional<z.ZodObject<{
|
|
200
|
+
accountUuid: z.ZodOptional<z.ZodString>;
|
|
201
|
+
emailAddress: z.ZodOptional<z.ZodString>;
|
|
202
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
203
|
+
organizationUuid: z.ZodOptional<z.ZodString>;
|
|
204
|
+
organizationName: z.ZodOptional<z.ZodString>;
|
|
205
|
+
organizationType: z.ZodOptional<z.ZodString>;
|
|
206
|
+
organizationRole: z.ZodOptional<z.ZodString>;
|
|
207
|
+
workspaceRole: z.ZodOptional<z.ZodString>;
|
|
208
|
+
billingType: z.ZodOptional<z.ZodString>;
|
|
209
|
+
seatTier: z.ZodOptional<z.ZodString>;
|
|
210
|
+
organizationRateLimitTier: z.ZodOptional<z.ZodString>;
|
|
211
|
+
userRateLimitTier: z.ZodOptional<z.ZodString>;
|
|
212
|
+
hasExtraUsageEnabled: z.ZodOptional<z.ZodBoolean>;
|
|
213
|
+
subscriptionCreatedAt: z.ZodOptional<z.ZodString>;
|
|
214
|
+
accountCreatedAt: z.ZodOptional<z.ZodString>;
|
|
215
|
+
}, z.core.$strict>>;
|
|
216
|
+
providers: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
217
|
+
lastRequestAt: z.ZodISODateTime;
|
|
218
|
+
lastStatus: z.ZodNumber;
|
|
219
|
+
lastModel: z.ZodOptional<z.ZodString>;
|
|
220
|
+
rateLimit: z.ZodOptional<z.ZodObject<{
|
|
221
|
+
observedAt: z.ZodISODateTime;
|
|
222
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
223
|
+
unified: z.ZodOptional<z.ZodObject<{
|
|
224
|
+
status: z.ZodOptional<z.ZodString>;
|
|
225
|
+
fiveHour: z.ZodOptional<z.ZodObject<{
|
|
226
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
227
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
228
|
+
status: z.ZodOptional<z.ZodString>;
|
|
229
|
+
}, z.core.$strict>>;
|
|
230
|
+
sevenDay: z.ZodOptional<z.ZodObject<{
|
|
231
|
+
utilization: z.ZodOptional<z.ZodNumber>;
|
|
232
|
+
resetsAt: z.ZodOptional<z.ZodISODateTime>;
|
|
233
|
+
status: z.ZodOptional<z.ZodString>;
|
|
234
|
+
}, z.core.$strict>>;
|
|
235
|
+
representativeClaim: z.ZodOptional<z.ZodString>;
|
|
236
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
237
|
+
overageStatus: z.ZodOptional<z.ZodString>;
|
|
238
|
+
}, z.core.$strict>>;
|
|
239
|
+
}, z.core.$strict>>;
|
|
240
|
+
lastLimit: z.ZodOptional<z.ZodObject<{
|
|
241
|
+
kind: z.ZodEnum<{
|
|
242
|
+
"rate-limited": "rate-limited";
|
|
243
|
+
"quota-exhausted": "quota-exhausted";
|
|
244
|
+
}>;
|
|
245
|
+
retryAfterSeconds: z.ZodOptional<z.ZodNumber>;
|
|
246
|
+
resetAt: z.ZodOptional<z.ZodISODateTime>;
|
|
247
|
+
window: z.ZodOptional<z.ZodString>;
|
|
248
|
+
evidence: z.ZodArray<z.ZodString>;
|
|
249
|
+
observedAt: z.ZodISODateTime;
|
|
250
|
+
status: z.ZodNumber;
|
|
251
|
+
}, z.core.$strict>>;
|
|
252
|
+
}, z.core.$strict>>;
|
|
253
|
+
}, z.core.$strict>;
|
|
254
|
+
export type UsageSnapshot = z.infer<typeof UsageSnapshotSchema>;
|
|
255
|
+
/**
|
|
256
|
+
* Whether a response header is one the store keeps: the quota and rate-limit families the upstreams claude-use 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.
|
|
257
|
+
*/
|
|
258
|
+
export declare function isRateLimitHeader(name: string): boolean;
|
|
259
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-use",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Joseph Mearman <joseph@mearman.co.uk>",
|