@bitkyc08/opencodex 2.63.0 → 2.64.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/AGENTS_INSTALL.md +4 -3
- package/README.md +46 -33
- package/assets/download-linux.svg +10 -0
- package/assets/download-macos.svg +10 -0
- package/assets/download-windows.svg +10 -0
- package/bin/ocx.mjs +12 -4
- package/gui/dist/assets/App-CpuDF3ci.js +50 -0
- package/gui/dist/assets/App-I5AnaSLh.css +1 -0
- package/gui/dist/assets/{Tray-CncKDBTp.js → Tray-B4uEIa1O.js} +1 -1
- package/gui/dist/assets/index-DiBRuK-d.css +1 -0
- package/gui/dist/assets/index-SggB6t3z.js +86 -0
- package/gui/dist/assets/usage-companion-chart-DLbKOJml.js +1 -0
- package/gui/dist/index.html +2 -2
- package/native/remote-workspace-helper/src/protocol.rs +5 -0
- package/package.json +4 -1
- package/src/adapters/anthropic.ts +66 -7
- package/src/adapters/codebuddy/adapter.ts +112 -16
- package/src/adapters/codebuddy/mcp-server.ts +180 -0
- package/src/adapters/codebuddy/scaffold-guard.ts +25 -8
- package/src/adapters/codebuddy/tool-bridge.ts +597 -0
- package/src/adapters/coding-agent/protocol.ts +123 -10
- package/src/adapters/coding-agent/turn.ts +324 -2
- package/src/adapters/command-code-restored-schema.ts +112 -0
- package/src/adapters/command-code-tool-text.ts +589 -0
- package/src/adapters/command-code.ts +80 -6
- package/src/adapters/cursor/catalog.ts +12 -0
- package/src/adapters/cursor/current-request.ts +46 -0
- package/src/adapters/cursor/discovery.ts +1 -1
- package/src/adapters/cursor/effort-map.ts +4 -0
- package/src/adapters/cursor/native-exec.ts +15 -0
- package/src/adapters/cursor/protobuf-events.ts +40 -12
- package/src/adapters/cursor/protobuf-request.ts +83 -18
- package/src/adapters/cursor/request-builder.ts +5 -4
- package/src/adapters/cursor/tool-guidance.ts +1 -1
- package/src/adapters/devin/live-models.ts +7 -0
- package/src/adapters/inline-think-tags.ts +251 -0
- package/src/adapters/kiro/adapter.ts +1 -0
- package/src/adapters/kiro/stream.ts +5 -3
- package/src/adapters/kiro/usage.ts +4 -3
- package/src/adapters/mimo-free.ts +37 -18
- package/src/adapters/openai-chat/messages.ts +5 -5
- package/src/adapters/openai-chat/tool-schema.ts +124 -2
- package/src/adapters/openai-chat.ts +13 -3
- package/src/adapters/openai-responses/passthrough.ts +15 -2
- package/src/adapters/openai-responses/reasoning.ts +14 -1
- package/src/adapters/openai-responses/request-strips.ts +29 -3
- package/src/adapters/openai-responses/tool-output-recovery.ts +9 -3
- package/src/adapters/qoder/adapter.ts +15 -12
- package/src/adapters/qoder/scaffold-guard.ts +45 -13
- package/src/bridge/internal.ts +20 -0
- package/src/bridge/sse.ts +14 -5
- package/src/claude/agents-inject.ts +2 -1
- package/src/claude/desktop-applied-marker.ts +44 -0
- package/src/claude/desktop-profile.ts +22 -12
- package/src/claude/inbound.ts +8 -3
- package/src/cli/access.ts +22 -5
- package/src/cli/account-api.ts +6 -0
- package/src/cli/account-auth.ts +5 -2
- package/src/cli/account.ts +5 -6
- package/src/cli/aside-profiles.ts +61 -1
- package/src/cli/claude-desktop.ts +3 -2
- package/src/cli/claude.ts +3 -1
- package/src/cli/codex-cli-update.ts +2 -2
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/dispatch.ts +12 -4
- package/src/cli/index.ts +98 -10
- package/src/cli/registry.ts +1 -1
- package/src/cli/resolve.ts +113 -1
- package/src/cli/root.ts +5 -0
- package/src/cli/stop-approval.ts +186 -0
- package/src/cli/stop-report.ts +1 -1
- package/src/cli/system-restart-client.ts +19 -4
- package/src/client/hub-client.ts +25 -18
- package/src/clients/config-export.ts +12 -4
- package/src/codex/account-auto-switch.ts +59 -0
- package/src/codex/account-lifecycle.ts +7 -0
- package/src/codex/account-priority.ts +10 -0
- package/src/codex/account-usability.ts +9 -0
- package/src/codex/auth-api/account-list.ts +5 -0
- package/src/codex/auth-api/login-flow.ts +15 -5
- package/src/codex/auth-api/login-state.ts +5 -16
- package/src/codex/auth-api/pool-mode-gate.ts +30 -7
- package/src/codex/auth-api/routes.ts +39 -3
- package/src/codex/auth-context.ts +145 -17
- package/src/codex/catalog/build-entries.ts +2 -1
- package/src/codex/catalog/effort.ts +8 -2
- package/src/codex/catalog/metadata.ts +33 -7
- package/src/codex/catalog/native-models.ts +102 -5
- package/src/codex/catalog/parsing.ts +2 -0
- package/src/codex/catalog/provider-models.ts +19 -6
- package/src/codex/cli-installation-targets.ts +44 -40
- package/src/codex/codex-write-lock.ts +32 -2
- package/src/codex/inject/multi-agent-v2.ts +90 -0
- package/src/codex/inject/plan.ts +365 -0
- package/src/codex/inject.ts +237 -362
- package/src/codex/log-guard/maintenance.ts +30 -13
- package/src/codex/project-config-warnings.ts +47 -7
- package/src/codex/prompt-layers/encoding.ts +41 -0
- package/src/codex/prompt-layers/toml-read.ts +1 -1
- package/src/codex/prompt-layers.ts +17 -13
- package/src/codex/prompt-text-probe.ts +20 -10
- package/src/codex/quota-observation-freshness.ts +34 -0
- package/src/codex/quota-rejection.ts +8 -4
- package/src/codex/quota.ts +4 -2
- package/src/codex/routing/selection.ts +32 -9
- package/src/codex/routing.ts +53 -11
- package/src/codex/subagent-model-fallback.ts +4 -3
- package/src/codex/sync.ts +11 -0
- package/src/codex/windows-installation-files.ts +33 -11
- package/src/codex/write-coordination.ts +4 -0
- package/src/combos/request.ts +6 -2
- package/src/companion/settings.ts +6 -1
- package/src/config/atomic-write.ts +12 -1
- package/src/config/derived-registries.ts +29 -0
- package/src/config/diagnostics.ts +17 -0
- package/src/config/live-reconcile.ts +11 -2
- package/src/config/load-degrade.ts +9 -2
- package/src/config/persist-unlocked.ts +4 -3
- package/src/config/persisted-mutation.ts +94 -0
- package/src/config/provider-validation.ts +1 -1
- package/src/config/proxy-env.ts +62 -12
- package/src/config/rebase-provenance.ts +80 -1
- package/src/config/schema/config-schema.ts +5 -0
- package/src/config/schema/leaf-validators.ts +50 -1
- package/src/config/subagent-models.ts +41 -15
- package/src/config.ts +18 -95
- package/src/generated/compatibility-version.json +330 -218
- package/src/generated/model-metadata.ts +8 -5
- package/src/github/star-state.ts +46 -6
- package/src/grok/inject.ts +74 -40
- package/src/grok/status.ts +11 -4
- package/src/integrations/cursor-effort-table.ts +50 -13
- package/src/integrations/raycast-detect.ts +19 -4
- package/src/integrations/serialize.ts +11 -1
- package/src/lib/bounded-body.ts +30 -0
- package/src/lib/crash-guard.ts +52 -4
- package/src/lib/local-aside-sync-contract.ts +41 -0
- package/src/lib/package-tree-integrity.ts +181 -9
- package/src/lib/proxy-env.ts +18 -1
- package/src/lib/request-failure-attribution.ts +1 -0
- package/src/lib/request-failure-model.ts +3 -0
- package/src/lib/service-secrets.ts +99 -2
- package/src/lib/socks5-fetch.ts +43 -14
- package/src/lib/token-estimate.ts +17 -2
- package/src/oauth/command-code.ts +3 -2
- package/src/oauth/generic-account-failover.ts +29 -1
- package/src/oauth/index.ts +24 -16
- package/src/oauth/login-flow-state.ts +5 -5
- package/src/oauth/meta-muse-device.ts +49 -7
- package/src/oauth/store.ts +74 -9
- package/src/providers/alibaba-region-backup.ts +16 -1
- package/src/providers/anthropic-fast.ts +89 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +288 -0
- package/src/providers/anthropic-reset-grants.ts +329 -0
- package/src/providers/claude-cli-identity.ts +12 -0
- package/src/providers/command-code-efforts.ts +184 -102
- package/src/providers/derive.ts +11 -3
- package/src/providers/fastwire.ts +16 -3
- package/src/providers/label.ts +8 -3
- package/src/providers/model-rename-fields.ts +1 -0
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/quota/vendor-probes-oauth.ts +2 -1
- package/src/providers/reasoning-metadata.ts +38 -15
- package/src/providers/registry/entries-core.ts +95 -7
- package/src/providers/registry/entries-extended.ts +21 -10
- package/src/providers/registry/model-ids.ts +1 -0
- package/src/providers/registry/model-seeds.ts +31 -3
- package/src/providers/registry/types.ts +3 -1
- package/src/providers/resolved-model-policy-merge.ts +38 -8
- package/src/providers/resolved-model-policy.ts +4 -2
- package/src/providers/service-tier.ts +3 -1
- package/src/reasoning-effort.ts +6 -5
- package/src/responses/code-mode-helper-compat.ts +14 -4
- package/src/responses/code-mode-shell-input.ts +54 -0
- package/src/responses/custom-tool-compat.ts +173 -5
- package/src/responses/parser.ts +38 -2
- package/src/responses/reasoning-replay-cache.ts +26 -0
- package/src/router.ts +11 -1
- package/src/routing/history/indexer.ts +7 -2
- package/src/routing/history/schema.ts +3 -1
- package/src/server/adapter-resolve.ts +2 -2
- package/src/server/auth-cors.ts +3 -0
- package/src/server/chat-completions.ts +14 -1
- package/src/server/chat-native.ts +17 -0
- package/src/server/claude-messages.ts +4 -3
- package/src/server/direct-local-http.ts +45 -19
- package/src/server/gui-session.ts +6 -15
- package/src/server/index/package-tree-guard.ts +53 -0
- package/src/server/index/serve-options.ts +17 -3
- package/src/server/index/startup-warnings.ts +17 -0
- package/src/server/index.ts +15 -17
- package/src/server/lifecycle.ts +8 -0
- package/src/server/live.ts +54 -4
- package/src/server/management/agent-settings-routes.ts +44 -8
- package/src/server/management/anthropic-reset-grant-routes.ts +252 -0
- package/src/server/management/codex-prompt-routes.ts +5 -1
- package/src/server/management/config-routes.ts +14 -4
- package/src/server/management/oauth-account-routes.ts +17 -1
- package/src/server/management/provider-overwrite-carry.ts +164 -0
- package/src/server/management/provider-routes.ts +36 -6
- package/src/server/management/remote-workspace-routes.ts +2 -2
- package/src/server/management/route-registry.ts +2 -0
- package/src/server/management/shadow-call-validation.ts +39 -0
- package/src/server/management/system-restart.ts +72 -6
- package/src/server/management-api.ts +16 -2
- package/src/server/management-auth.ts +52 -8
- package/src/server/proxy-liveness.ts +110 -4
- package/src/server/request-log.ts +26 -9
- package/src/server/request-metrics.ts +5 -0
- package/src/server/responses/adapter-continuation.ts +6 -3
- package/src/server/responses/adapter-dispatch.ts +65 -1
- package/src/server/responses/compact.ts +31 -1
- package/src/server/responses/compaction-routing.ts +28 -3
- package/src/server/responses/core-codex-account.ts +85 -26
- package/src/server/responses/core-combo-failure.ts +16 -15
- package/src/server/responses/core-normalize.ts +5 -1
- package/src/server/responses/core-opaque-recovery.ts +50 -2
- package/src/server/responses/core-options.ts +2 -0
- package/src/server/responses/core-replay.ts +7 -0
- package/src/server/responses/core.ts +1 -1
- package/src/server/responses/encrypted-payload.ts +23 -9
- package/src/server/responses/passthrough-delivery.ts +55 -35
- package/src/server/responses/passthrough-dispatch.ts +12 -13
- package/src/server/responses/policy-fallback.ts +12 -1
- package/src/server/responses/request-prepare.ts +46 -7
- package/src/server/responses/request-spend.ts +31 -15
- package/src/server/responses/run-turn-execution.ts +6 -5
- package/src/server/responses/shadow-target-availability.ts +61 -0
- package/src/server/responses-custom-tool-repair.ts +2 -0
- package/src/service/claim.ts +185 -0
- package/src/service/cli.ts +10 -1
- package/src/service/guarded-manager-target.ts +151 -0
- package/src/service/guards.ts +47 -48
- package/src/service/managing-cli.ts +170 -0
- package/src/service/orchestration.ts +3 -1
- package/src/service/state.ts +4 -0
- package/src/service/systemd.ts +16 -1
- package/src/service.ts +1 -1
- package/src/types/config.ts +8 -0
- package/src/types/provider.ts +16 -0
- package/src/types/request.ts +10 -0
- package/src/types/tools.ts +9 -1
- package/src/types/wire.ts +68 -4
- package/src/types.ts +1 -0
- package/src/update/job.ts +10 -16
- package/src/update/npm-invocation.mjs +17 -16
- package/src/update/transactional-install.d.mts +22 -1
- package/src/update/transactional-install.mjs +176 -19
- package/src/update/update-failure-guidance.d.mts +8 -0
- package/src/update/update-failure-guidance.mjs +47 -0
- package/src/usage/expected-prices.ts +54 -12
- package/src/usage/log.ts +117 -3
- package/src/usage/telemetry-contract.ts +1 -0
- package/src/usage/timeline.ts +34 -6
- package/src/usage/user-cost-overlay-reconciler.ts +3 -3
- package/src/vision/reasoning.ts +2 -4
- package/src/web-search/xai-executor.ts +14 -4
- package/gui/dist/assets/App-BqrsSrIR.js +0 -50
- package/gui/dist/assets/index-C6SJrh0N.js +0 -86
- package/gui/dist/assets/index-DdDunwDb.css +0 -1
- package/gui/dist/assets/usage-companion-chart-CzAAAB1o.js +0 -1
- package/src/adapters/kiro-thinking.ts +0 -112
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic usage-limit reset grants (upstream program `cedar_ember`).
|
|
3
|
+
*
|
|
4
|
+
* Claude Pro/Max/Team subscriptions can carry a one-time grant that clears the
|
|
5
|
+
* 5-hour and weekly usage windows. The contract here mirrors the Claude Code CLI
|
|
6
|
+
* (2.1.278) byte for byte where it matters:
|
|
7
|
+
*
|
|
8
|
+
* - read: GET /api/oauth/usage?cedar_ember=1&skip_spend=1 (block `cedar_ember`)
|
|
9
|
+
* - org: GET /api/oauth/profile (`organization.uuid`)
|
|
10
|
+
* - redeem: POST /api/organizations/{org}/reset_rate_limits
|
|
11
|
+
* body {program:"cedar_ember", grant_id, request_id}
|
|
12
|
+
*
|
|
13
|
+
* Parsing is fail-closed: a malformed block is an error, never "zero grants",
|
|
14
|
+
* because the dashboard spends from what this module returns. Nothing here logs,
|
|
15
|
+
* and no upstream body, token, or organization id leaves through an error message.
|
|
16
|
+
*/
|
|
17
|
+
import { CLAUDE_CLI_USER_AGENT } from "./claude-cli-identity";
|
|
18
|
+
|
|
19
|
+
export const ANTHROPIC_API_ORIGIN = "https://api.anthropic.com";
|
|
20
|
+
export const ANTHROPIC_RESET_GRANT_PROGRAM = "cedar_ember";
|
|
21
|
+
export const ANTHROPIC_RESET_GRANT_STATUS_PATH = "/api/oauth/usage?cedar_ember=1&skip_spend=1";
|
|
22
|
+
export const ANTHROPIC_PROFILE_PATH = "/api/oauth/profile";
|
|
23
|
+
/** Same bound the Claude Code client uses for the claim. */
|
|
24
|
+
export const ANTHROPIC_RESET_GRANT_REDEEM_TIMEOUT_MS = 25_000;
|
|
25
|
+
const READ_TIMEOUT_MS = 12_000;
|
|
26
|
+
|
|
27
|
+
export const ANTHROPIC_RESET_GRANT_ID_RE = /^[a-z0-9_-]{1,40}$/;
|
|
28
|
+
export const ANTHROPIC_RESET_REQUEST_ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
29
|
+
const ORGANIZATION_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
30
|
+
|
|
31
|
+
/** Usage windows a grant can clear. Others upstream may add are dropped. */
|
|
32
|
+
export const ANTHROPIC_RESET_WINDOWS = ["five_hour", "seven_day", "seven_day_overage_included"] as const;
|
|
33
|
+
export type AnthropicResetWindow = typeof ANTHROPIC_RESET_WINDOWS[number];
|
|
34
|
+
|
|
35
|
+
/** Terminal answers the claim endpoint can give. */
|
|
36
|
+
export const ANTHROPIC_RESET_RESULTS = ["reset", "already_used", "not_limited", "cooldown", "ineligible", "unavailable"] as const;
|
|
37
|
+
export type AnthropicResetUpstreamResult = typeof ANTHROPIC_RESET_RESULTS[number];
|
|
38
|
+
/** Upstream results plus the two HTTP refusals that prove nothing was spent. */
|
|
39
|
+
export type AnthropicResetSettledCode = AnthropicResetUpstreamResult | "rate_limited" | "auth_error";
|
|
40
|
+
|
|
41
|
+
export interface AnthropicResetGrant {
|
|
42
|
+
id: string;
|
|
43
|
+
label: string;
|
|
44
|
+
resetsTotal: number;
|
|
45
|
+
resetsLeft: number;
|
|
46
|
+
startsAt: string | null;
|
|
47
|
+
endsAt: string | null;
|
|
48
|
+
clears: AnthropicResetWindow[];
|
|
49
|
+
paused: boolean;
|
|
50
|
+
usableNow: boolean;
|
|
51
|
+
useRequiresLimit: boolean;
|
|
52
|
+
percentUsed: Partial<Record<AnthropicResetWindow, number>>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface AnthropicResetGrantStatus {
|
|
56
|
+
eligible: boolean;
|
|
57
|
+
ineligibleReason: string | null;
|
|
58
|
+
atLimit: boolean;
|
|
59
|
+
grants: AnthropicResetGrant[];
|
|
60
|
+
nextGrantId: string | null;
|
|
61
|
+
weeklyResetsAt: string | null;
|
|
62
|
+
cooldownUntil: string | null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type AnthropicResetGrantErrorCode = "auth" | "upstream" | "malformed";
|
|
66
|
+
|
|
67
|
+
/** A read failure. `message` is fixed text; upstream detail is never attached. */
|
|
68
|
+
export class AnthropicResetGrantError extends Error {
|
|
69
|
+
constructor(readonly code: AnthropicResetGrantErrorCode) {
|
|
70
|
+
super(`Anthropic reset-grant read failed (${code})`);
|
|
71
|
+
this.name = "AnthropicResetGrantError";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The claim was sent (or may have been) and no terminal answer came back. */
|
|
76
|
+
export class AnthropicResetGrantUnknownOutcome extends Error {
|
|
77
|
+
constructor() {
|
|
78
|
+
super("Anthropic reset-grant claim outcome is unknown");
|
|
79
|
+
this.name = "AnthropicResetGrantUnknownOutcome";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const KNOWN_INELIGIBLE_REASONS = new Set([
|
|
84
|
+
"config_off", "tier", "seat", "mobile", "surface", "cli_version", "no_grant",
|
|
85
|
+
"tenure", "other_experiment", "unavailable", "unknown",
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
89
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isCount(value: unknown): value is number {
|
|
93
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Optional ISO timestamp: absent/null → null, anything unparsable rejects the block. */
|
|
97
|
+
function optionalTimestamp(value: unknown): string | null | undefined {
|
|
98
|
+
if (value === undefined || value === null) return null;
|
|
99
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value))) return undefined;
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function optionalBoolean(value: unknown, fallback: boolean): boolean | undefined {
|
|
104
|
+
if (value === undefined || value === null) return fallback;
|
|
105
|
+
return typeof value === "boolean" ? value : undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Upstream display text, stripped of control characters and bounded. */
|
|
109
|
+
function safeLabel(value: unknown): string {
|
|
110
|
+
if (typeof value !== "string") return "";
|
|
111
|
+
return value.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ").replace(/\s+/g, " ").trim().slice(0, 120);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function parseWindows(value: unknown): AnthropicResetWindow[] | undefined {
|
|
115
|
+
if (value === undefined || value === null) return [];
|
|
116
|
+
if (!Array.isArray(value)) return undefined;
|
|
117
|
+
return ANTHROPIC_RESET_WINDOWS.filter(window => value.includes(window));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function parsePercentUsed(value: unknown): Partial<Record<AnthropicResetWindow, number>> {
|
|
121
|
+
const out: Partial<Record<AnthropicResetWindow, number>> = {};
|
|
122
|
+
if (!isRecord(value)) return out;
|
|
123
|
+
for (const window of ANTHROPIC_RESET_WINDOWS) {
|
|
124
|
+
const percent = value[window];
|
|
125
|
+
if (typeof percent === "number" && Number.isInteger(percent) && percent >= 0 && percent <= 100) out[window] = percent;
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function parseGrant(value: unknown): AnthropicResetGrant | null {
|
|
131
|
+
if (!isRecord(value)) return null;
|
|
132
|
+
const { id, resets_total: total, resets_left: left } = value;
|
|
133
|
+
if (typeof id !== "string" || !ANTHROPIC_RESET_GRANT_ID_RE.test(id)) return null;
|
|
134
|
+
if (!isCount(total) || !isCount(left) || left > total) return null;
|
|
135
|
+
const startsAt = optionalTimestamp(value.starts_at);
|
|
136
|
+
const endsAt = optionalTimestamp(value.ends_at);
|
|
137
|
+
const clears = parseWindows(value.clears);
|
|
138
|
+
const paused = optionalBoolean(value.paused, false);
|
|
139
|
+
// Missing usability flags default to the refusing side: a grant that does not
|
|
140
|
+
// say it is usable is not offered for spending.
|
|
141
|
+
const usableNow = optionalBoolean(value.usable_now, false);
|
|
142
|
+
const useRequiresLimit = optionalBoolean(value.use_requires_limit, true);
|
|
143
|
+
if (startsAt === undefined || endsAt === undefined || clears === undefined
|
|
144
|
+
|| paused === undefined || usableNow === undefined || useRequiresLimit === undefined) return null;
|
|
145
|
+
return {
|
|
146
|
+
id,
|
|
147
|
+
label: safeLabel(value.label),
|
|
148
|
+
resetsTotal: total,
|
|
149
|
+
resetsLeft: left,
|
|
150
|
+
startsAt,
|
|
151
|
+
endsAt,
|
|
152
|
+
clears,
|
|
153
|
+
paused,
|
|
154
|
+
usableNow,
|
|
155
|
+
useRequiresLimit,
|
|
156
|
+
percentUsed: parsePercentUsed(value.percent_used),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Parses the `cedar_ember` block. Returns null for a missing or malformed block;
|
|
162
|
+
* one malformed grant, a duplicate id, or a bad timestamp rejects the whole block.
|
|
163
|
+
*/
|
|
164
|
+
export function parseAnthropicResetGrantStatus(block: unknown): AnthropicResetGrantStatus | null {
|
|
165
|
+
if (!isRecord(block) || typeof block.eligible !== "boolean") return null;
|
|
166
|
+
const rawGrants = block.grants ?? [];
|
|
167
|
+
if (!Array.isArray(rawGrants)) return null;
|
|
168
|
+
const grants: AnthropicResetGrant[] = [];
|
|
169
|
+
const seen = new Set<string>();
|
|
170
|
+
for (const raw of rawGrants) {
|
|
171
|
+
const grant = parseGrant(raw);
|
|
172
|
+
if (!grant || seen.has(grant.id)) return null;
|
|
173
|
+
seen.add(grant.id);
|
|
174
|
+
grants.push(grant);
|
|
175
|
+
}
|
|
176
|
+
const reason = block.ineligible_reason;
|
|
177
|
+
if (reason !== undefined && reason !== null && typeof reason !== "string") return null;
|
|
178
|
+
const atLimit = optionalBoolean(block.at_limit, false);
|
|
179
|
+
const weeklyResetsAt = optionalTimestamp(block.weekly_resets_at);
|
|
180
|
+
const cooldownUntil = optionalTimestamp(block.cooldown_until);
|
|
181
|
+
if (atLimit === undefined || weeklyResetsAt === undefined || cooldownUntil === undefined) return null;
|
|
182
|
+
const next = block.next_grant_id;
|
|
183
|
+
return {
|
|
184
|
+
eligible: block.eligible,
|
|
185
|
+
ineligibleReason: typeof reason === "string" ? (KNOWN_INELIGIBLE_REASONS.has(reason) ? reason : "unknown") : null,
|
|
186
|
+
atLimit,
|
|
187
|
+
grants,
|
|
188
|
+
nextGrantId: typeof next === "string" && seen.has(next) ? next : null,
|
|
189
|
+
weeklyResetsAt,
|
|
190
|
+
cooldownUntil,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Why a grant cannot be spent right now, or null when it can. */
|
|
195
|
+
export function anthropicResetGrantBlocker(
|
|
196
|
+
status: AnthropicResetGrantStatus,
|
|
197
|
+
grantId: string,
|
|
198
|
+
): "ineligible" | "unknown_grant" | "paused" | "not_usable" | "exhausted" | "not_limited" | null {
|
|
199
|
+
if (!status.eligible) return "ineligible";
|
|
200
|
+
const grant = status.grants.find(candidate => candidate.id === grantId);
|
|
201
|
+
if (!grant) return "unknown_grant";
|
|
202
|
+
if (grant.paused) return "paused";
|
|
203
|
+
if (!grant.usableNow) return "not_usable";
|
|
204
|
+
if (grant.resetsLeft <= 0) return "exhausted";
|
|
205
|
+
if (grant.useRequiresLimit && !status.atLimit) return "not_limited";
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export interface AnthropicAccountRequestOptions {
|
|
210
|
+
accessToken: string;
|
|
211
|
+
fetchFn?: typeof globalThis.fetch;
|
|
212
|
+
signal?: AbortSignal;
|
|
213
|
+
origin?: string;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function accountHeaders(accessToken: string, json: boolean): Record<string, string> {
|
|
217
|
+
return {
|
|
218
|
+
Accept: "application/json",
|
|
219
|
+
...(json ? { "Content-Type": "application/json" } : {}),
|
|
220
|
+
"User-Agent": CLAUDE_CLI_USER_AGENT,
|
|
221
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
222
|
+
Authorization: `Bearer ${accessToken}`,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function readSignal(signal: AbortSignal | undefined, timeoutMs: number): AbortSignal {
|
|
227
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
228
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function getAccountJson(path: string, options: AnthropicAccountRequestOptions): Promise<unknown> {
|
|
232
|
+
const fetchImpl = options.fetchFn ?? globalThis.fetch;
|
|
233
|
+
let response: Response;
|
|
234
|
+
try {
|
|
235
|
+
response = await fetchImpl(`${options.origin ?? ANTHROPIC_API_ORIGIN}${path}`, {
|
|
236
|
+
method: "GET",
|
|
237
|
+
headers: accountHeaders(options.accessToken, false),
|
|
238
|
+
signal: readSignal(options.signal, READ_TIMEOUT_MS),
|
|
239
|
+
});
|
|
240
|
+
} catch {
|
|
241
|
+
throw new AnthropicResetGrantError("upstream");
|
|
242
|
+
}
|
|
243
|
+
if (response.status === 401 || response.status === 403) throw new AnthropicResetGrantError("auth");
|
|
244
|
+
if (!response.ok) throw new AnthropicResetGrantError("upstream");
|
|
245
|
+
try {
|
|
246
|
+
return await response.json();
|
|
247
|
+
} catch {
|
|
248
|
+
throw new AnthropicResetGrantError("malformed");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Reads the reset-grant block for the account that owns `accessToken`. */
|
|
253
|
+
export async function fetchAnthropicResetGrantStatus(options: AnthropicAccountRequestOptions): Promise<AnthropicResetGrantStatus> {
|
|
254
|
+
const body = await getAccountJson(ANTHROPIC_RESET_GRANT_STATUS_PATH, options);
|
|
255
|
+
const status = isRecord(body) ? parseAnthropicResetGrantStatus(body.cedar_ember) : null;
|
|
256
|
+
if (!status) throw new AnthropicResetGrantError("malformed");
|
|
257
|
+
return status;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** The organization the claim is filed against, from the token's own profile. */
|
|
261
|
+
export async function fetchAnthropicOrganizationUuid(options: AnthropicAccountRequestOptions): Promise<string> {
|
|
262
|
+
const body = await getAccountJson(ANTHROPIC_PROFILE_PATH, options);
|
|
263
|
+
const organization = isRecord(body) ? body.organization : undefined;
|
|
264
|
+
const uuid = isRecord(organization) ? organization.uuid : undefined;
|
|
265
|
+
if (typeof uuid !== "string" || !ORGANIZATION_UUID_RE.test(uuid)) throw new AnthropicResetGrantError("malformed");
|
|
266
|
+
return uuid.toLowerCase();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface AnthropicResetClaimOptions extends AnthropicAccountRequestOptions {
|
|
270
|
+
organizationUuid: string;
|
|
271
|
+
grantId: string;
|
|
272
|
+
requestId: string;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export interface AnthropicResetClaimAnswer {
|
|
276
|
+
code: AnthropicResetSettledCode;
|
|
277
|
+
resetsLeft: number | null;
|
|
278
|
+
cleared: AnthropicResetWindow[];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Files one claim. Resolves only with a terminal answer; throws
|
|
283
|
+
* {@link AnthropicResetGrantUnknownOutcome} whenever the claim may have run
|
|
284
|
+
* upstream without an answer we can read (transport error, timeout, 5xx,
|
|
285
|
+
* unreadable body). Validation failures throw before anything is sent.
|
|
286
|
+
*/
|
|
287
|
+
export async function claimAnthropicResetGrant(options: AnthropicResetClaimOptions): Promise<AnthropicResetClaimAnswer> {
|
|
288
|
+
if (!ORGANIZATION_UUID_RE.test(options.organizationUuid)
|
|
289
|
+
|| !ANTHROPIC_RESET_GRANT_ID_RE.test(options.grantId)
|
|
290
|
+
|| !ANTHROPIC_RESET_REQUEST_ID_RE.test(options.requestId)) {
|
|
291
|
+
throw new AnthropicResetGrantError("malformed");
|
|
292
|
+
}
|
|
293
|
+
const fetchImpl = options.fetchFn ?? globalThis.fetch;
|
|
294
|
+
const url = `${options.origin ?? ANTHROPIC_API_ORIGIN}/api/organizations/${encodeURIComponent(options.organizationUuid)}/reset_rate_limits`;
|
|
295
|
+
let response: Response;
|
|
296
|
+
try {
|
|
297
|
+
response = await fetchImpl(url, {
|
|
298
|
+
method: "POST",
|
|
299
|
+
headers: accountHeaders(options.accessToken, true),
|
|
300
|
+
body: JSON.stringify({
|
|
301
|
+
program: ANTHROPIC_RESET_GRANT_PROGRAM,
|
|
302
|
+
grant_id: options.grantId,
|
|
303
|
+
request_id: options.requestId,
|
|
304
|
+
}),
|
|
305
|
+
signal: readSignal(options.signal, ANTHROPIC_RESET_GRANT_REDEEM_TIMEOUT_MS),
|
|
306
|
+
});
|
|
307
|
+
} catch {
|
|
308
|
+
throw new AnthropicResetGrantUnknownOutcome();
|
|
309
|
+
}
|
|
310
|
+
// Refusals that prove the claim did not run, exactly as the Claude Code client maps them.
|
|
311
|
+
if (response.status === 429) return { code: "rate_limited", resetsLeft: null, cleared: [] };
|
|
312
|
+
if (response.status === 401 || response.status === 403) return { code: "auth_error", resetsLeft: null, cleared: [] };
|
|
313
|
+
if (!response.ok) throw new AnthropicResetGrantUnknownOutcome();
|
|
314
|
+
let body: unknown;
|
|
315
|
+
try {
|
|
316
|
+
body = await response.json();
|
|
317
|
+
} catch {
|
|
318
|
+
throw new AnthropicResetGrantUnknownOutcome();
|
|
319
|
+
}
|
|
320
|
+
if (!isRecord(body) || typeof body.result !== "string"
|
|
321
|
+
|| !(ANTHROPIC_RESET_RESULTS as readonly string[]).includes(body.result)) {
|
|
322
|
+
throw new AnthropicResetGrantUnknownOutcome();
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
code: body.result as AnthropicResetUpstreamResult,
|
|
326
|
+
resetsLeft: isCount(body.resets_left) ? body.resets_left : null,
|
|
327
|
+
cleared: parseWindows(body.cleared) ?? [],
|
|
328
|
+
};
|
|
329
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Claude Code client identity opencodex presents on Anthropic account
|
|
3
|
+
* endpoints (`/api/oauth/usage`, `/api/oauth/profile`, the reset-grant claim).
|
|
4
|
+
*
|
|
5
|
+
* Upstream gates some response blocks on this header. With `claude-cli/2.1.63` the
|
|
6
|
+
* usage endpoint answers the reset-grant block with `ineligible_reason:
|
|
7
|
+
* "cli_version"`; `claude-code/<ver>` is answered with `"surface"`. Only the
|
|
8
|
+
* `claude-cli/<ver> (external, cli)` form of a current release is treated as the
|
|
9
|
+
* Claude Code CLI surface. Bump the version together with a live GET check.
|
|
10
|
+
*/
|
|
11
|
+
export const CLAUDE_CLI_VERSION = "2.1.280";
|
|
12
|
+
export const CLAUDE_CLI_USER_AGENT = `claude-cli/${CLAUDE_CLI_VERSION} (external, cli)`;
|