codex-multi-auth 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -0
- package/README.md +162 -0
- package/assets/opencode-logo-ornate-dark.svg +18 -0
- package/assets/readme-hero.svg +31 -0
- package/config/README.md +87 -0
- package/config/minimal-opencode.json +13 -0
- package/config/opencode-legacy.json +571 -0
- package/config/opencode-modern.json +239 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3160 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/accounts/rate-limits.d.ts +22 -0
- package/dist/lib/accounts/rate-limits.d.ts.map +1 -0
- package/dist/lib/accounts/rate-limits.js +63 -0
- package/dist/lib/accounts/rate-limits.js.map +1 -0
- package/dist/lib/accounts.d.ts +95 -0
- package/dist/lib/accounts.d.ts.map +1 -0
- package/dist/lib/accounts.js +668 -0
- package/dist/lib/accounts.js.map +1 -0
- package/dist/lib/audit.d.ts +45 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +131 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/auth/auth.d.ts +56 -0
- package/dist/lib/auth/auth.d.ts.map +1 -0
- package/dist/lib/auth/auth.js +214 -0
- package/dist/lib/auth/auth.js.map +1 -0
- package/dist/lib/auth/browser.d.ts +34 -0
- package/dist/lib/auth/browser.d.ts.map +1 -0
- package/dist/lib/auth/browser.js +185 -0
- package/dist/lib/auth/browser.js.map +1 -0
- package/dist/lib/auth/server.d.ts +24 -0
- package/dist/lib/auth/server.d.ts.map +1 -0
- package/dist/lib/auth/server.js +116 -0
- package/dist/lib/auth/server.js.map +1 -0
- package/dist/lib/auth/token-utils.d.ts +59 -0
- package/dist/lib/auth/token-utils.d.ts.map +1 -0
- package/dist/lib/auth/token-utils.js +331 -0
- package/dist/lib/auth/token-utils.js.map +1 -0
- package/dist/lib/auth-rate-limit.d.ts +20 -0
- package/dist/lib/auth-rate-limit.d.ts.map +1 -0
- package/dist/lib/auth-rate-limit.js +91 -0
- package/dist/lib/auth-rate-limit.js.map +1 -0
- package/dist/lib/auto-update-checker.d.ts +10 -0
- package/dist/lib/auto-update-checker.d.ts.map +1 -0
- package/dist/lib/auto-update-checker.js +216 -0
- package/dist/lib/auto-update-checker.js.map +1 -0
- package/dist/lib/capability-policy.d.ts +18 -0
- package/dist/lib/capability-policy.d.ts.map +1 -0
- package/dist/lib/capability-policy.js +150 -0
- package/dist/lib/capability-policy.js.map +1 -0
- package/dist/lib/circuit-breaker.d.ts +34 -0
- package/dist/lib/circuit-breaker.d.ts.map +1 -0
- package/dist/lib/circuit-breaker.js +124 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/cli.d.ts +64 -0
- package/dist/lib/cli.d.ts.map +1 -0
- package/dist/lib/cli.js +274 -0
- package/dist/lib/cli.js.map +1 -0
- package/dist/lib/codex-cli/observability.d.ts +22 -0
- package/dist/lib/codex-cli/observability.d.ts.map +1 -0
- package/dist/lib/codex-cli/observability.js +36 -0
- package/dist/lib/codex-cli/observability.js.map +1 -0
- package/dist/lib/codex-cli/state.d.ts +86 -0
- package/dist/lib/codex-cli/state.d.ts.map +1 -0
- package/dist/lib/codex-cli/state.js +470 -0
- package/dist/lib/codex-cli/state.js.map +1 -0
- package/dist/lib/codex-cli/sync.d.ts +27 -0
- package/dist/lib/codex-cli/sync.d.ts.map +1 -0
- package/dist/lib/codex-cli/sync.js +325 -0
- package/dist/lib/codex-cli/sync.js.map +1 -0
- package/dist/lib/codex-cli/writer.d.ts +12 -0
- package/dist/lib/codex-cli/writer.d.ts.map +1 -0
- package/dist/lib/codex-cli/writer.js +388 -0
- package/dist/lib/codex-cli/writer.js.map +1 -0
- package/dist/lib/codex-manager.d.ts +2 -0
- package/dist/lib/codex-manager.d.ts.map +1 -0
- package/dist/lib/codex-manager.js +4841 -0
- package/dist/lib/codex-manager.js.map +1 -0
- package/dist/lib/config.d.ts +269 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +789 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +78 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +78 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/context-overflow.d.ts +27 -0
- package/dist/lib/context-overflow.d.ts.map +1 -0
- package/dist/lib/context-overflow.js +124 -0
- package/dist/lib/context-overflow.js.map +1 -0
- package/dist/lib/dashboard-settings.d.ts +90 -0
- package/dist/lib/dashboard-settings.d.ts.map +1 -0
- package/dist/lib/dashboard-settings.js +327 -0
- package/dist/lib/dashboard-settings.js.map +1 -0
- package/dist/lib/entitlement-cache.d.ts +41 -0
- package/dist/lib/entitlement-cache.d.ts.map +1 -0
- package/dist/lib/entitlement-cache.js +137 -0
- package/dist/lib/entitlement-cache.js.map +1 -0
- package/dist/lib/errors.d.ts +113 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +103 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/forecast.d.ts +42 -0
- package/dist/lib/forecast.d.ts.map +1 -0
- package/dist/lib/forecast.js +256 -0
- package/dist/lib/forecast.js.map +1 -0
- package/dist/lib/health.d.ts +33 -0
- package/dist/lib/health.d.ts.map +1 -0
- package/dist/lib/health.js +70 -0
- package/dist/lib/health.js.map +1 -0
- package/dist/lib/index.d.ts +32 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +32 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/live-account-sync.d.ts +39 -0
- package/dist/lib/live-account-sync.d.ts.map +1 -0
- package/dist/lib/live-account-sync.js +196 -0
- package/dist/lib/live-account-sync.js.map +1 -0
- package/dist/lib/logger.d.ts +40 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +364 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/oauth-success.html +338 -0
- package/dist/lib/parallel-probe.d.ts +28 -0
- package/dist/lib/parallel-probe.d.ts.map +1 -0
- package/dist/lib/parallel-probe.js +97 -0
- package/dist/lib/parallel-probe.js.map +1 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts +53 -0
- package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -0
- package/dist/lib/preemptive-quota-scheduler.js +220 -0
- package/dist/lib/preemptive-quota-scheduler.js.map +1 -0
- package/dist/lib/proactive-refresh.d.ts +66 -0
- package/dist/lib/proactive-refresh.d.ts.map +1 -0
- package/dist/lib/proactive-refresh.js +143 -0
- package/dist/lib/proactive-refresh.js.map +1 -0
- package/dist/lib/prompts/codex-opencode-bridge.d.ts +19 -0
- package/dist/lib/prompts/codex-opencode-bridge.d.ts.map +1 -0
- package/dist/lib/prompts/codex-opencode-bridge.js +169 -0
- package/dist/lib/prompts/codex-opencode-bridge.js.map +1 -0
- package/dist/lib/prompts/codex.d.ts +41 -0
- package/dist/lib/prompts/codex.d.ts.map +1 -0
- package/dist/lib/prompts/codex.js +383 -0
- package/dist/lib/prompts/codex.js.map +1 -0
- package/dist/lib/prompts/opencode-codex.d.ts +25 -0
- package/dist/lib/prompts/opencode-codex.d.ts.map +1 -0
- package/dist/lib/prompts/opencode-codex.js +270 -0
- package/dist/lib/prompts/opencode-codex.js.map +1 -0
- package/dist/lib/quota-cache.d.ts +68 -0
- package/dist/lib/quota-cache.d.ts.map +1 -0
- package/dist/lib/quota-cache.js +224 -0
- package/dist/lib/quota-cache.js.map +1 -0
- package/dist/lib/quota-probe.d.ts +49 -0
- package/dist/lib/quota-probe.d.ts.map +1 -0
- package/dist/lib/quota-probe.js +368 -0
- package/dist/lib/quota-probe.js.map +1 -0
- package/dist/lib/recovery/constants.d.ts +12 -0
- package/dist/lib/recovery/constants.d.ts.map +1 -0
- package/dist/lib/recovery/constants.js +31 -0
- package/dist/lib/recovery/constants.js.map +1 -0
- package/dist/lib/recovery/index.d.ts +12 -0
- package/dist/lib/recovery/index.d.ts.map +1 -0
- package/dist/lib/recovery/index.js +12 -0
- package/dist/lib/recovery/index.js.map +1 -0
- package/dist/lib/recovery/storage.d.ts +24 -0
- package/dist/lib/recovery/storage.d.ts.map +1 -0
- package/dist/lib/recovery/storage.js +362 -0
- package/dist/lib/recovery/storage.js.map +1 -0
- package/dist/lib/recovery/types.d.ts +116 -0
- package/dist/lib/recovery/types.d.ts.map +1 -0
- package/dist/lib/recovery/types.js +7 -0
- package/dist/lib/recovery/types.js.map +1 -0
- package/dist/lib/recovery.d.ts +31 -0
- package/dist/lib/recovery.d.ts.map +1 -0
- package/dist/lib/recovery.js +313 -0
- package/dist/lib/recovery.js.map +1 -0
- package/dist/lib/refresh-guardian.d.ts +31 -0
- package/dist/lib/refresh-guardian.d.ts.map +1 -0
- package/dist/lib/refresh-guardian.js +151 -0
- package/dist/lib/refresh-guardian.js.map +1 -0
- package/dist/lib/refresh-lease.d.ts +37 -0
- package/dist/lib/refresh-lease.d.ts.map +1 -0
- package/dist/lib/refresh-lease.js +335 -0
- package/dist/lib/refresh-lease.js.map +1 -0
- package/dist/lib/refresh-queue.d.ts +117 -0
- package/dist/lib/refresh-queue.d.ts.map +1 -0
- package/dist/lib/refresh-queue.js +297 -0
- package/dist/lib/refresh-queue.js.map +1 -0
- package/dist/lib/request/failure-policy.d.ts +42 -0
- package/dist/lib/request/failure-policy.d.ts.map +1 -0
- package/dist/lib/request/failure-policy.js +133 -0
- package/dist/lib/request/failure-policy.js.map +1 -0
- package/dist/lib/request/fetch-helpers.d.ts +152 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -0
- package/dist/lib/request/fetch-helpers.js +704 -0
- package/dist/lib/request/fetch-helpers.js.map +1 -0
- package/dist/lib/request/helpers/input-utils.d.ts +7 -0
- package/dist/lib/request/helpers/input-utils.d.ts.map +1 -0
- package/dist/lib/request/helpers/input-utils.js +214 -0
- package/dist/lib/request/helpers/input-utils.js.map +1 -0
- package/dist/lib/request/helpers/model-map.d.ts +28 -0
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -0
- package/dist/lib/request/helpers/model-map.js +133 -0
- package/dist/lib/request/helpers/model-map.js.map +1 -0
- package/dist/lib/request/helpers/tool-utils.d.ts +29 -0
- package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -0
- package/dist/lib/request/helpers/tool-utils.js +117 -0
- package/dist/lib/request/helpers/tool-utils.js.map +1 -0
- package/dist/lib/request/rate-limit-backoff.d.ts +17 -0
- package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -0
- package/dist/lib/request/rate-limit-backoff.js +83 -0
- package/dist/lib/request/rate-limit-backoff.js.map +1 -0
- package/dist/lib/request/request-transformer.d.ts +107 -0
- package/dist/lib/request/request-transformer.d.ts.map +1 -0
- package/dist/lib/request/request-transformer.js +814 -0
- package/dist/lib/request/request-transformer.js.map +1 -0
- package/dist/lib/request/response-handler.d.ts +23 -0
- package/dist/lib/request/response-handler.d.ts.map +1 -0
- package/dist/lib/request/response-handler.js +155 -0
- package/dist/lib/request/response-handler.js.map +1 -0
- package/dist/lib/request/stream-failover.d.ts +21 -0
- package/dist/lib/request/stream-failover.d.ts.map +1 -0
- package/dist/lib/request/stream-failover.js +204 -0
- package/dist/lib/request/stream-failover.js.map +1 -0
- package/dist/lib/rotation.d.ts +146 -0
- package/dist/lib/rotation.d.ts.map +1 -0
- package/dist/lib/rotation.js +321 -0
- package/dist/lib/rotation.js.map +1 -0
- package/dist/lib/runtime-paths.d.ts +58 -0
- package/dist/lib/runtime-paths.d.ts.map +1 -0
- package/dist/lib/runtime-paths.js +164 -0
- package/dist/lib/runtime-paths.js.map +1 -0
- package/dist/lib/schemas.d.ts +435 -0
- package/dist/lib/schemas.d.ts.map +1 -0
- package/dist/lib/schemas.js +268 -0
- package/dist/lib/schemas.js.map +1 -0
- package/dist/lib/session-affinity.d.ts +23 -0
- package/dist/lib/session-affinity.d.ts.map +1 -0
- package/dist/lib/session-affinity.js +127 -0
- package/dist/lib/session-affinity.js.map +1 -0
- package/dist/lib/shutdown.d.ts +7 -0
- package/dist/lib/shutdown.d.ts.map +1 -0
- package/dist/lib/shutdown.js +43 -0
- package/dist/lib/shutdown.js.map +1 -0
- package/dist/lib/storage/migrations.d.ts +59 -0
- package/dist/lib/storage/migrations.d.ts.map +1 -0
- package/dist/lib/storage/migrations.js +41 -0
- package/dist/lib/storage/migrations.js.map +1 -0
- package/dist/lib/storage/paths.d.ts +51 -0
- package/dist/lib/storage/paths.d.ts.map +1 -0
- package/dist/lib/storage/paths.js +152 -0
- package/dist/lib/storage/paths.js.map +1 -0
- package/dist/lib/storage.d.ts +106 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib/storage.js +896 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/table-formatter.d.ts +32 -0
- package/dist/lib/table-formatter.d.ts.map +1 -0
- package/dist/lib/table-formatter.js +44 -0
- package/dist/lib/table-formatter.js.map +1 -0
- package/dist/lib/tools/hashline-tools.d.ts +51 -0
- package/dist/lib/tools/hashline-tools.d.ts.map +1 -0
- package/dist/lib/tools/hashline-tools.js +456 -0
- package/dist/lib/tools/hashline-tools.js.map +1 -0
- package/dist/lib/types.d.ts +130 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/ui/ansi.d.ts +40 -0
- package/dist/lib/ui/ansi.d.ts.map +1 -0
- package/dist/lib/ui/ansi.js +68 -0
- package/dist/lib/ui/ansi.js.map +1 -0
- package/dist/lib/ui/auth-menu.d.ts +76 -0
- package/dist/lib/ui/auth-menu.d.ts.map +1 -0
- package/dist/lib/ui/auth-menu.js +590 -0
- package/dist/lib/ui/auth-menu.js.map +1 -0
- package/dist/lib/ui/confirm.d.ts +11 -0
- package/dist/lib/ui/confirm.d.ts.map +1 -0
- package/dist/lib/ui/confirm.js +29 -0
- package/dist/lib/ui/confirm.js.map +1 -0
- package/dist/lib/ui/copy.d.ts +123 -0
- package/dist/lib/ui/copy.d.ts.map +1 -0
- package/dist/lib/ui/copy.js +127 -0
- package/dist/lib/ui/copy.js.map +1 -0
- package/dist/lib/ui/format.d.ts +62 -0
- package/dist/lib/ui/format.d.ts.map +1 -0
- package/dist/lib/ui/format.js +205 -0
- package/dist/lib/ui/format.js.map +1 -0
- package/dist/lib/ui/runtime.d.ts +43 -0
- package/dist/lib/ui/runtime.d.ts.map +1 -0
- package/dist/lib/ui/runtime.js +69 -0
- package/dist/lib/ui/runtime.js.map +1 -0
- package/dist/lib/ui/select.d.ts +60 -0
- package/dist/lib/ui/select.d.ts.map +1 -0
- package/dist/lib/ui/select.js +467 -0
- package/dist/lib/ui/select.js.map +1 -0
- package/dist/lib/ui/theme.d.ts +56 -0
- package/dist/lib/ui/theme.d.ts.map +1 -0
- package/dist/lib/ui/theme.js +186 -0
- package/dist/lib/ui/theme.js.map +1 -0
- package/dist/lib/unified-settings.d.ts +71 -0
- package/dist/lib/unified-settings.d.ts.map +1 -0
- package/dist/lib/unified-settings.js +299 -0
- package/dist/lib/unified-settings.js.map +1 -0
- package/dist/lib/utils.d.ts +29 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +54 -0
- package/dist/lib/utils.js.map +1 -0
- package/package.json +115 -0
- package/scripts/audit-dev-allowlist.js +128 -0
- package/scripts/bench-format/hashline-v2.mjs +642 -0
- package/scripts/bench-format/models.mjs +105 -0
- package/scripts/bench-format/opencode.mjs +205 -0
- package/scripts/bench-format/render.mjs +496 -0
- package/scripts/bench-format/stats.mjs +54 -0
- package/scripts/bench-format/tasks.mjs +151 -0
- package/scripts/benchmark-edit-formats.mjs +1161 -0
- package/scripts/benchmark-render-dashboard.mjs +49 -0
- package/scripts/codex-multi-auth.js +6 -0
- package/scripts/codex-routing.js +34 -0
- package/scripts/codex.js +122 -0
- package/scripts/copy-oauth-success.js +37 -0
- package/scripts/install-opencode-codex-auth.js +193 -0
- package/scripts/test-all-models.sh +7 -0
- package/scripts/test-model-matrix.js +424 -0
- package/scripts/validate-model-map.sh +7 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { existsSync, promises as fs } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getCodexMultiAuthDir } from "./runtime-paths.js";
|
|
4
|
+
import { logWarn } from "./logger.js";
|
|
5
|
+
import { sleep } from "./utils.js";
|
|
6
|
+
import { getUnifiedSettingsPath, loadUnifiedDashboardSettings, saveUnifiedDashboardSettings, } from "./unified-settings.js";
|
|
7
|
+
export const DASHBOARD_DISPLAY_SETTINGS_VERSION = 1;
|
|
8
|
+
export const DEFAULT_DASHBOARD_DISPLAY_SETTINGS = {
|
|
9
|
+
showPerAccountRows: true,
|
|
10
|
+
showQuotaDetails: true,
|
|
11
|
+
showForecastReasons: true,
|
|
12
|
+
showRecommendations: true,
|
|
13
|
+
showLiveProbeNotes: true,
|
|
14
|
+
actionAutoReturnMs: 2_000,
|
|
15
|
+
actionPauseOnKey: true,
|
|
16
|
+
menuAutoFetchLimits: true,
|
|
17
|
+
menuSortEnabled: true,
|
|
18
|
+
menuSortMode: "ready-first",
|
|
19
|
+
menuSortPinCurrent: false,
|
|
20
|
+
menuSortQuickSwitchVisibleRow: true,
|
|
21
|
+
uiThemePreset: "green",
|
|
22
|
+
uiAccentColor: "green",
|
|
23
|
+
menuShowStatusBadge: true,
|
|
24
|
+
menuShowCurrentBadge: true,
|
|
25
|
+
menuShowLastUsed: true,
|
|
26
|
+
menuShowQuotaSummary: true,
|
|
27
|
+
menuShowQuotaCooldown: true,
|
|
28
|
+
menuShowFetchStatus: true,
|
|
29
|
+
menuShowDetailsForUnselectedRows: false,
|
|
30
|
+
menuLayoutMode: "compact-details",
|
|
31
|
+
menuQuotaTtlMs: 5 * 60_000,
|
|
32
|
+
menuFocusStyle: "row-invert",
|
|
33
|
+
menuHighlightCurrentRow: true,
|
|
34
|
+
menuStatuslineFields: ["last-used", "limits", "status"],
|
|
35
|
+
};
|
|
36
|
+
const DASHBOARD_SETTINGS_PATH = join(getCodexMultiAuthDir(), "dashboard-settings.json");
|
|
37
|
+
const RETRYABLE_READ_CODES = new Set(["EBUSY", "EPERM", "EAGAIN"]);
|
|
38
|
+
const LEGACY_READ_MAX_ATTEMPTS = 4;
|
|
39
|
+
const LEGACY_READ_BASE_DELAY_MS = 20;
|
|
40
|
+
/**
|
|
41
|
+
* Checks whether a value is a non-null object that can be treated as a string-keyed record.
|
|
42
|
+
*
|
|
43
|
+
* @param value - Value to test
|
|
44
|
+
* @returns `true` if `value` is an object and not `null` (narrowed to `Record<string, unknown>`), `false` otherwise.
|
|
45
|
+
*/
|
|
46
|
+
function isRecord(value) {
|
|
47
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
48
|
+
}
|
|
49
|
+
function isRetryableReadError(error) {
|
|
50
|
+
const code = error?.code;
|
|
51
|
+
return typeof code === "string" && RETRYABLE_READ_CODES.has(code);
|
|
52
|
+
}
|
|
53
|
+
async function readLegacySettingsFile(path) {
|
|
54
|
+
let lastError;
|
|
55
|
+
for (let attempt = 0; attempt < LEGACY_READ_MAX_ATTEMPTS; attempt += 1) {
|
|
56
|
+
try {
|
|
57
|
+
return await fs.readFile(path, "utf8");
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (!isRetryableReadError(error) || attempt + 1 >= LEGACY_READ_MAX_ATTEMPTS) {
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
lastError = error;
|
|
64
|
+
await sleep(LEGACY_READ_BASE_DELAY_MS * 2 ** attempt);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
throw lastError instanceof Error ? lastError : new Error("Failed to read legacy dashboard settings");
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Coerces a value to a boolean, using a provided fallback when the input is not a boolean.
|
|
71
|
+
*
|
|
72
|
+
* @param value - The input to evaluate; only `true` and `false` are accepted as boolean values
|
|
73
|
+
* @param fallback - The boolean to return when `value` is not a boolean
|
|
74
|
+
* @returns The boolean `value` if it is a boolean, otherwise `fallback`
|
|
75
|
+
*/
|
|
76
|
+
function normalizeBoolean(value, fallback) {
|
|
77
|
+
return typeof value === "boolean" ? value : fallback;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Normalize an input value into a dashboard theme preset.
|
|
81
|
+
*
|
|
82
|
+
* @param value - Untrusted input to coerce to a theme preset
|
|
83
|
+
* @returns `"blue"` if `value` is exactly `"blue"`, `"green"` otherwise
|
|
84
|
+
*/
|
|
85
|
+
function normalizeThemePreset(value) {
|
|
86
|
+
return value === "blue" ? "blue" : "green";
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Normalize an input value into an allowed dashboard accent color, defaulting to green.
|
|
90
|
+
*
|
|
91
|
+
* @param value - Input value to coerce into an accent color
|
|
92
|
+
* @returns `'cyan'`, `'blue'`, or `'yellow'` if `value` matches one of those strings; otherwise `'green'`
|
|
93
|
+
*/
|
|
94
|
+
function normalizeAccentColor(value) {
|
|
95
|
+
switch (value) {
|
|
96
|
+
case "cyan":
|
|
97
|
+
return "cyan";
|
|
98
|
+
case "blue":
|
|
99
|
+
return "blue";
|
|
100
|
+
case "yellow":
|
|
101
|
+
return "yellow";
|
|
102
|
+
default:
|
|
103
|
+
return "green";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Validate a dashboard layout mode, allowing only "expanded-rows".
|
|
108
|
+
*
|
|
109
|
+
* @param value - Candidate value to validate as a layout mode
|
|
110
|
+
* @param fallback - Value to return when `value` is not `"expanded-rows"`
|
|
111
|
+
* @returns `"expanded-rows"` if `value` strictly equals that string, otherwise `fallback`
|
|
112
|
+
*/
|
|
113
|
+
function normalizeLayoutMode(value, fallback) {
|
|
114
|
+
return value === "expanded-rows" ? "expanded-rows" : fallback;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Coerces an arbitrary value into the dashboard focus style.
|
|
118
|
+
*
|
|
119
|
+
* @param value - Value to coerce into a DashboardFocusStyle
|
|
120
|
+
* @returns The normalized focus style; always `'row-invert'`.
|
|
121
|
+
*/
|
|
122
|
+
function normalizeFocusStyle(value) {
|
|
123
|
+
return value === "row-invert" ? "row-invert" : "row-invert";
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Normalize a numeric TTL (milliseconds) into a validated, rounded, bounded integer.
|
|
127
|
+
*
|
|
128
|
+
* @param value - The input value to normalize; if not a finite number, the function falls back.
|
|
129
|
+
* @param fallback - Value returned when `value` is not a finite number.
|
|
130
|
+
* @returns A rounded integer number of milliseconds between 60,000 and 1,800,000 (30 minutes), or `fallback` if `value` is invalid.
|
|
131
|
+
*/
|
|
132
|
+
function normalizeQuotaTtlMs(value, fallback) {
|
|
133
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
134
|
+
return fallback;
|
|
135
|
+
const rounded = Math.round(value);
|
|
136
|
+
return Math.max(60_000, Math.min(30 * 60_000, rounded));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Normalize a candidate account sort mode to a known mode.
|
|
140
|
+
*
|
|
141
|
+
* @param value - The input value to validate as an account sort mode
|
|
142
|
+
* @param fallback - Mode to use if `value` is not a recognized sort mode
|
|
143
|
+
* @returns The validated `DashboardAccountSortMode`: `ready-first` or `manual` if `value` matches, otherwise `fallback`
|
|
144
|
+
*/
|
|
145
|
+
function normalizeAccountSortMode(value, fallback) {
|
|
146
|
+
if (value === "ready-first" || value === "manual") {
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
return fallback;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Coerces an untrusted value to an integer millisecond timeout between 0 and 10000.
|
|
153
|
+
*
|
|
154
|
+
* This rounds and clamps numeric input; if the input is not a finite number the provided
|
|
155
|
+
* fallback is returned. Safe to call concurrently. Filesystem semantics, Windows atomicity,
|
|
156
|
+
* and unified-settings token redaction are not applicable to this operation.
|
|
157
|
+
*
|
|
158
|
+
* @param value - The untrusted input to normalize
|
|
159
|
+
* @param fallback - The numeric fallback to use when `value` is invalid
|
|
160
|
+
* @returns A millisecond timeout value between 0 and 10000 inclusive
|
|
161
|
+
*/
|
|
162
|
+
function normalizeAutoReturnMs(value, fallback) {
|
|
163
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
164
|
+
return fallback;
|
|
165
|
+
const rounded = Math.round(value);
|
|
166
|
+
return Math.max(0, Math.min(10_000, rounded));
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Coerces an arbitrary value into a deduplicated, order-preserving list of allowed statusline fields.
|
|
170
|
+
*
|
|
171
|
+
* @param value - The input to normalize; may be any value. Non-string entries, unknown fields, and duplicates are discarded.
|
|
172
|
+
* @returns An array of `DashboardStatuslineField` in their original order with duplicates removed. If the input is not an array or yields no valid fields, returns the default statusline fields.
|
|
173
|
+
*/
|
|
174
|
+
function normalizeStatuslineFields(value) {
|
|
175
|
+
const defaultFields = [...(DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuStatuslineFields ?? [])];
|
|
176
|
+
if (!Array.isArray(value))
|
|
177
|
+
return defaultFields;
|
|
178
|
+
const allowed = new Set(["last-used", "limits", "status"]);
|
|
179
|
+
const fields = [];
|
|
180
|
+
for (const entry of value) {
|
|
181
|
+
if (typeof entry !== "string")
|
|
182
|
+
continue;
|
|
183
|
+
if (!allowed.has(entry))
|
|
184
|
+
continue;
|
|
185
|
+
const typed = entry;
|
|
186
|
+
if (!fields.includes(typed)) {
|
|
187
|
+
fields.push(typed);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return fields.length > 0 ? fields : defaultFields;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Convert a DashboardDisplaySettings object into a plain record suitable for JSON serialization and persistence.
|
|
194
|
+
*
|
|
195
|
+
* @param value - The dashboard display settings to serialize
|
|
196
|
+
* @returns A shallow Record<string, unknown> containing the same keys and values as `value`, ready for JSON encoding
|
|
197
|
+
*
|
|
198
|
+
* Notes:
|
|
199
|
+
* - This function performs no I/O and is safe to call concurrently.
|
|
200
|
+
* - It does not perform any token redaction or platform-specific path normalization; behavior is identical across platforms (including Windows).
|
|
201
|
+
*/
|
|
202
|
+
function toJsonRecord(value) {
|
|
203
|
+
const record = {};
|
|
204
|
+
for (const [key, fieldValue] of Object.entries(value)) {
|
|
205
|
+
record[key] = fieldValue;
|
|
206
|
+
}
|
|
207
|
+
return record;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Filesystem path to the unified dashboard settings file.
|
|
211
|
+
*
|
|
212
|
+
* This path is the canonical location used by the load/save helpers. Callers should assume the file may be concurrently modified by other processes and use atomic write strategies when persisting. On Windows the returned path may contain backslashes; redact any sensitive tokens before logging or external reporting.
|
|
213
|
+
*
|
|
214
|
+
* @returns The absolute path to the unified dashboard settings file.
|
|
215
|
+
*/
|
|
216
|
+
export function getDashboardSettingsPath() {
|
|
217
|
+
return getUnifiedSettingsPath();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Normalize an untrusted value into a complete, validated DashboardDisplaySettings object.
|
|
221
|
+
*
|
|
222
|
+
* Produces a settings object where missing or invalid fields are replaced with sensible defaults,
|
|
223
|
+
* layout-derived fields are resolved, numeric values are clamped to allowed ranges, and enumerations
|
|
224
|
+
* are coerced to allowed values.
|
|
225
|
+
*
|
|
226
|
+
* Concurrency: pure and deterministic; safe to call concurrently. This function performs no I/O,
|
|
227
|
+
* so Windows filesystem semantics do not apply. It does not perform token redaction or any
|
|
228
|
+
* sensitive-data filtering.
|
|
229
|
+
*
|
|
230
|
+
* @param value - The input to normalize (may be any type, typically parsed JSON or a partial settings record)
|
|
231
|
+
* @returns A DashboardDisplaySettings object with all fields validated and defaulted
|
|
232
|
+
*/
|
|
233
|
+
export function normalizeDashboardDisplaySettings(value) {
|
|
234
|
+
if (!isRecord(value)) {
|
|
235
|
+
return { ...DEFAULT_DASHBOARD_DISPLAY_SETTINGS };
|
|
236
|
+
}
|
|
237
|
+
const derivedLayoutMode = normalizeLayoutMode(value.menuLayoutMode, value.menuShowDetailsForUnselectedRows === true ? "expanded-rows" : "compact-details");
|
|
238
|
+
return {
|
|
239
|
+
showPerAccountRows: normalizeBoolean(value.showPerAccountRows, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.showPerAccountRows),
|
|
240
|
+
showQuotaDetails: normalizeBoolean(value.showQuotaDetails, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.showQuotaDetails),
|
|
241
|
+
showForecastReasons: normalizeBoolean(value.showForecastReasons, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.showForecastReasons),
|
|
242
|
+
showRecommendations: normalizeBoolean(value.showRecommendations, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.showRecommendations),
|
|
243
|
+
showLiveProbeNotes: normalizeBoolean(value.showLiveProbeNotes, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.showLiveProbeNotes),
|
|
244
|
+
actionAutoReturnMs: normalizeAutoReturnMs(value.actionAutoReturnMs, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.actionAutoReturnMs ?? 2_000),
|
|
245
|
+
actionPauseOnKey: normalizeBoolean(value.actionPauseOnKey, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.actionPauseOnKey ?? true),
|
|
246
|
+
menuAutoFetchLimits: normalizeBoolean(value.menuAutoFetchLimits, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuAutoFetchLimits ?? true),
|
|
247
|
+
menuSortEnabled: normalizeBoolean(value.menuSortEnabled, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortEnabled ?? false),
|
|
248
|
+
menuSortMode: normalizeAccountSortMode(value.menuSortMode, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortMode ?? "ready-first"),
|
|
249
|
+
menuSortPinCurrent: normalizeBoolean(value.menuSortPinCurrent, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortPinCurrent ?? true),
|
|
250
|
+
menuSortQuickSwitchVisibleRow: normalizeBoolean(value.menuSortQuickSwitchVisibleRow, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortQuickSwitchVisibleRow ?? true),
|
|
251
|
+
uiThemePreset: normalizeThemePreset(value.uiThemePreset),
|
|
252
|
+
uiAccentColor: normalizeAccentColor(value.uiAccentColor),
|
|
253
|
+
menuShowStatusBadge: normalizeBoolean(value.menuShowStatusBadge, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuShowStatusBadge ?? true),
|
|
254
|
+
menuShowCurrentBadge: normalizeBoolean(value.menuShowCurrentBadge, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuShowCurrentBadge ?? true),
|
|
255
|
+
menuShowLastUsed: normalizeBoolean(value.menuShowLastUsed, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuShowLastUsed ?? true),
|
|
256
|
+
menuShowQuotaSummary: normalizeBoolean(value.menuShowQuotaSummary, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuShowQuotaSummary ?? true),
|
|
257
|
+
menuShowQuotaCooldown: normalizeBoolean(value.menuShowQuotaCooldown, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuShowQuotaCooldown ?? true),
|
|
258
|
+
menuShowFetchStatus: normalizeBoolean(value.menuShowFetchStatus, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuShowFetchStatus ?? true),
|
|
259
|
+
menuShowDetailsForUnselectedRows: derivedLayoutMode === "expanded-rows",
|
|
260
|
+
menuLayoutMode: derivedLayoutMode,
|
|
261
|
+
menuQuotaTtlMs: normalizeQuotaTtlMs(value.menuQuotaTtlMs, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuQuotaTtlMs ?? 5 * 60_000),
|
|
262
|
+
menuFocusStyle: normalizeFocusStyle(value.menuFocusStyle),
|
|
263
|
+
menuHighlightCurrentRow: normalizeBoolean(value.menuHighlightCurrentRow, DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuHighlightCurrentRow ?? true),
|
|
264
|
+
menuStatuslineFields: normalizeStatuslineFields(value.menuStatuslineFields),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Load and return the normalized dashboard display settings, migrating legacy settings when present.
|
|
269
|
+
*
|
|
270
|
+
* Attempts to read unified settings first; if absent, loads legacy dashboard-settings.json, normalizes
|
|
271
|
+
* its contents, and tries to migrate the normalized result to the unified settings store. On any read,
|
|
272
|
+
* parse, or validation error, or if no settings are found, returns the built-in defaults.
|
|
273
|
+
*
|
|
274
|
+
* @returns The normalized DashboardDisplaySettings to apply.
|
|
275
|
+
*
|
|
276
|
+
* @remarks
|
|
277
|
+
* - Concurrency: callers may race with other processes performing migration or writes; callers should
|
|
278
|
+
* not assume exclusive access and should tolerate eventual consistency.
|
|
279
|
+
* - Filesystem (Windows): legacy-path checks use case-insensitive filesystem semantics implicitly; ensure
|
|
280
|
+
* any external tooling accounts for that when placing or removing legacy files.
|
|
281
|
+
* - Token handling: persisted settings are stored as-is; remove or redact any sensitive tokens from
|
|
282
|
+
* settings before calling this function if they must not be written unmodified to persistent stores.
|
|
283
|
+
*/
|
|
284
|
+
export async function loadDashboardDisplaySettings() {
|
|
285
|
+
const unifiedSettings = await loadUnifiedDashboardSettings();
|
|
286
|
+
if (unifiedSettings) {
|
|
287
|
+
return normalizeDashboardDisplaySettings(unifiedSettings);
|
|
288
|
+
}
|
|
289
|
+
if (!existsSync(DASHBOARD_SETTINGS_PATH)) {
|
|
290
|
+
return { ...DEFAULT_DASHBOARD_DISPLAY_SETTINGS };
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const raw = await readLegacySettingsFile(DASHBOARD_SETTINGS_PATH);
|
|
294
|
+
const parsed = JSON.parse(raw);
|
|
295
|
+
if (!isRecord(parsed)) {
|
|
296
|
+
return { ...DEFAULT_DASHBOARD_DISPLAY_SETTINGS };
|
|
297
|
+
}
|
|
298
|
+
const normalized = normalizeDashboardDisplaySettings(parsed.settings);
|
|
299
|
+
try {
|
|
300
|
+
await saveUnifiedDashboardSettings(toJsonRecord(normalized));
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
// Keep legacy fallback behavior even if migration write fails.
|
|
304
|
+
}
|
|
305
|
+
return normalized;
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
logWarn(`Failed to load dashboard settings from ${DASHBOARD_SETTINGS_PATH}: ${error instanceof Error ? error.message : String(error)}`);
|
|
309
|
+
return { ...DEFAULT_DASHBOARD_DISPLAY_SETTINGS };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Persist normalized dashboard display settings to the unified settings store.
|
|
314
|
+
*
|
|
315
|
+
* Normalizes `settings` and writes the resulting record via the unified settings API.
|
|
316
|
+
* Concurrent callers may race and overwrite each other; callers should serialize updates
|
|
317
|
+
* when strong write ordering is required. On Windows, underlying filesystem writes may
|
|
318
|
+
* not be atomic across processes. Any sensitive tokens or secrets present in settings
|
|
319
|
+
* are written as provided; callers must remove or redact sensitive values before saving.
|
|
320
|
+
*
|
|
321
|
+
* @param settings - The dashboard display settings to normalize and save
|
|
322
|
+
*/
|
|
323
|
+
export async function saveDashboardDisplaySettings(settings) {
|
|
324
|
+
const normalized = normalizeDashboardDisplaySettings(settings);
|
|
325
|
+
await saveUnifiedDashboardSettings(toJsonRecord(normalized));
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=dashboard-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-settings.js","sourceRoot":"","sources":["../../lib/dashboard-settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface EntitlementBlock {
|
|
2
|
+
model: string;
|
|
3
|
+
blockedUntil: number;
|
|
4
|
+
reason: "unsupported-model" | "plan-entitlement";
|
|
5
|
+
updatedAt: number;
|
|
6
|
+
}
|
|
7
|
+
export interface EntitlementCacheSnapshot {
|
|
8
|
+
accounts: Record<string, EntitlementBlock[]>;
|
|
9
|
+
}
|
|
10
|
+
export interface EntitlementAccountRef {
|
|
11
|
+
accountId?: string;
|
|
12
|
+
email?: string;
|
|
13
|
+
index?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Derives a stable cache key for an entitlement account reference.
|
|
17
|
+
*
|
|
18
|
+
* Produces one of three deterministic keys:
|
|
19
|
+
* - `id:<trimmed accountId>` when `accountId` is present,
|
|
20
|
+
* - `email:<lowercased trimmed email>` when `email` is present and no `accountId`,
|
|
21
|
+
* - `idx:<non-negative integer>` otherwise (index defaults to 0).
|
|
22
|
+
*
|
|
23
|
+
* This function is pure and concurrency-safe; it performs no I/O and is not affected by Windows filesystem semantics. It does not redact secrets or tokens — values are only trimmed and, for emails, lowercased.
|
|
24
|
+
*
|
|
25
|
+
* @param ref - Reference identifying an account (may include `accountId`, `email`, or `index`)
|
|
26
|
+
* @returns A deterministic string key prefixed with `id:`, `email:`, or `idx:` as described above
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveEntitlementAccountKey(ref: EntitlementAccountRef): string;
|
|
29
|
+
export declare class EntitlementCache {
|
|
30
|
+
private readonly blocksByAccount;
|
|
31
|
+
markBlocked(accountKey: string, model: string, reason: EntitlementBlock["reason"], ttlMs?: number, now?: number): void;
|
|
32
|
+
clear(accountKey: string, model?: string): void;
|
|
33
|
+
isBlocked(accountKey: string, model: string, now?: number): {
|
|
34
|
+
blocked: boolean;
|
|
35
|
+
waitMs: number;
|
|
36
|
+
reason?: EntitlementBlock["reason"];
|
|
37
|
+
};
|
|
38
|
+
prune(now?: number): number;
|
|
39
|
+
snapshot(now?: number): EntitlementCacheSnapshot;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=entitlement-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlement-cache.d.ts","sourceRoot":"","sources":["../../lib/entitlement-cache.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,mBAAmB,GAAG,kBAAkB,CAAC;IACjD,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;CAC7C;AAKD,MAAM,WAAW,qBAAqB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAmBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,qBAAqB,GAAG,MAAM,CAO/E;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoD;IAEpF,WAAW,CACV,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAClC,KAAK,SAAuB,EAC5B,GAAG,SAAa,GACd,IAAI;IAqBP,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAc/C,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;KAAE;IAmBzI,KAAK,CAAC,GAAG,SAAa,GAAG,MAAM;IAmB/B,QAAQ,CAAC,GAAG,SAAa,GAAG,wBAAwB;CAUpD"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { createLogger } from "./logger.js";
|
|
2
|
+
const log = createLogger("entitlement-cache");
|
|
3
|
+
const DEFAULT_BLOCK_TTL_MS = 30 * 60_000;
|
|
4
|
+
const MAX_ACCOUNT_BUCKETS = 512;
|
|
5
|
+
/**
|
|
6
|
+
* Produce a normalized model identifier suitable for cache keys and comparisons.
|
|
7
|
+
*
|
|
8
|
+
* Trims and lowercases the input, uses the final path segment if a slash is present,
|
|
9
|
+
* and removes common variant suffixes such as `-none`, `-minimal`, `-low`, `-medium`, `-high`, and `-xhigh`.
|
|
10
|
+
*
|
|
11
|
+
* @param model - The raw model string to normalize; may be undefined or empty.
|
|
12
|
+
* @returns The simplified model identifier, or `null` if `model` is missing or empty after trimming.
|
|
13
|
+
*/
|
|
14
|
+
function normalizeModel(model) {
|
|
15
|
+
if (!model)
|
|
16
|
+
return null;
|
|
17
|
+
const trimmed = model.trim().toLowerCase();
|
|
18
|
+
if (!trimmed)
|
|
19
|
+
return null;
|
|
20
|
+
const stripped = trimmed.includes("/") ? (trimmed.split("/").pop() ?? trimmed) : trimmed;
|
|
21
|
+
return stripped.replace(/-(none|minimal|low|medium|high|xhigh)$/i, "");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Derives a stable cache key for an entitlement account reference.
|
|
25
|
+
*
|
|
26
|
+
* Produces one of three deterministic keys:
|
|
27
|
+
* - `id:<trimmed accountId>` when `accountId` is present,
|
|
28
|
+
* - `email:<lowercased trimmed email>` when `email` is present and no `accountId`,
|
|
29
|
+
* - `idx:<non-negative integer>` otherwise (index defaults to 0).
|
|
30
|
+
*
|
|
31
|
+
* This function is pure and concurrency-safe; it performs no I/O and is not affected by Windows filesystem semantics. It does not redact secrets or tokens — values are only trimmed and, for emails, lowercased.
|
|
32
|
+
*
|
|
33
|
+
* @param ref - Reference identifying an account (may include `accountId`, `email`, or `index`)
|
|
34
|
+
* @returns A deterministic string key prefixed with `id:`, `email:`, or `idx:` as described above
|
|
35
|
+
*/
|
|
36
|
+
export function resolveEntitlementAccountKey(ref) {
|
|
37
|
+
const accountId = typeof ref.accountId === "string" ? ref.accountId.trim() : "";
|
|
38
|
+
if (accountId)
|
|
39
|
+
return `id:${accountId}`;
|
|
40
|
+
const email = typeof ref.email === "string" ? ref.email.trim().toLowerCase() : "";
|
|
41
|
+
if (email)
|
|
42
|
+
return `email:${email}`;
|
|
43
|
+
const index = Number.isFinite(ref.index) ? Math.max(0, Math.floor(ref.index ?? 0)) : 0;
|
|
44
|
+
return `idx:${index}`;
|
|
45
|
+
}
|
|
46
|
+
export class EntitlementCache {
|
|
47
|
+
blocksByAccount = new Map();
|
|
48
|
+
markBlocked(accountKey, model, reason, ttlMs = DEFAULT_BLOCK_TTL_MS, now = Date.now()) {
|
|
49
|
+
const normalizedModel = normalizeModel(model);
|
|
50
|
+
if (!accountKey || !normalizedModel)
|
|
51
|
+
return;
|
|
52
|
+
const normalizedTtlMs = typeof ttlMs === "number" && Number.isFinite(ttlMs)
|
|
53
|
+
? Math.max(1_000, Math.floor(ttlMs))
|
|
54
|
+
: DEFAULT_BLOCK_TTL_MS;
|
|
55
|
+
if (this.blocksByAccount.size >= MAX_ACCOUNT_BUCKETS && !this.blocksByAccount.has(accountKey)) {
|
|
56
|
+
const first = this.blocksByAccount.keys().next().value;
|
|
57
|
+
if (typeof first === "string")
|
|
58
|
+
this.blocksByAccount.delete(first);
|
|
59
|
+
}
|
|
60
|
+
const existing = this.blocksByAccount.get(accountKey) ?? new Map();
|
|
61
|
+
existing.set(normalizedModel, {
|
|
62
|
+
model: normalizedModel,
|
|
63
|
+
blockedUntil: now + normalizedTtlMs,
|
|
64
|
+
reason,
|
|
65
|
+
updatedAt: now,
|
|
66
|
+
});
|
|
67
|
+
this.blocksByAccount.set(accountKey, existing);
|
|
68
|
+
}
|
|
69
|
+
clear(accountKey, model) {
|
|
70
|
+
if (!accountKey)
|
|
71
|
+
return;
|
|
72
|
+
if (!model) {
|
|
73
|
+
this.blocksByAccount.delete(accountKey);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const normalizedModel = normalizeModel(model);
|
|
77
|
+
if (!normalizedModel)
|
|
78
|
+
return;
|
|
79
|
+
const accountBlocks = this.blocksByAccount.get(accountKey);
|
|
80
|
+
if (!accountBlocks)
|
|
81
|
+
return;
|
|
82
|
+
accountBlocks.delete(normalizedModel);
|
|
83
|
+
if (accountBlocks.size === 0)
|
|
84
|
+
this.blocksByAccount.delete(accountKey);
|
|
85
|
+
}
|
|
86
|
+
isBlocked(accountKey, model, now = Date.now()) {
|
|
87
|
+
const normalizedModel = normalizeModel(model);
|
|
88
|
+
if (!accountKey || !normalizedModel)
|
|
89
|
+
return { blocked: false, waitMs: 0 };
|
|
90
|
+
const accountBlocks = this.blocksByAccount.get(accountKey);
|
|
91
|
+
if (!accountBlocks)
|
|
92
|
+
return { blocked: false, waitMs: 0 };
|
|
93
|
+
const block = accountBlocks.get(normalizedModel);
|
|
94
|
+
if (!block)
|
|
95
|
+
return { blocked: false, waitMs: 0 };
|
|
96
|
+
if (block.blockedUntil <= now) {
|
|
97
|
+
accountBlocks.delete(normalizedModel);
|
|
98
|
+
if (accountBlocks.size === 0)
|
|
99
|
+
this.blocksByAccount.delete(accountKey);
|
|
100
|
+
return { blocked: false, waitMs: 0 };
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
blocked: true,
|
|
104
|
+
waitMs: Math.max(0, block.blockedUntil - now),
|
|
105
|
+
reason: block.reason,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
prune(now = Date.now()) {
|
|
109
|
+
let removed = 0;
|
|
110
|
+
for (const [accountKey, blocks] of this.blocksByAccount.entries()) {
|
|
111
|
+
for (const [model, block] of blocks.entries()) {
|
|
112
|
+
if (block.blockedUntil <= now) {
|
|
113
|
+
blocks.delete(model);
|
|
114
|
+
removed += 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (blocks.size === 0) {
|
|
118
|
+
this.blocksByAccount.delete(accountKey);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (removed > 0) {
|
|
122
|
+
log.debug("Pruned entitlement cache", { removed });
|
|
123
|
+
}
|
|
124
|
+
return removed;
|
|
125
|
+
}
|
|
126
|
+
snapshot(now = Date.now()) {
|
|
127
|
+
this.prune(now);
|
|
128
|
+
const accounts = {};
|
|
129
|
+
for (const [accountKey, blocks] of this.blocksByAccount.entries()) {
|
|
130
|
+
accounts[accountKey] = Array.from(blocks.values())
|
|
131
|
+
.map((block) => ({ ...block }))
|
|
132
|
+
.sort((a, b) => a.model.localeCompare(b.model));
|
|
133
|
+
}
|
|
134
|
+
return { accounts };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=entitlement-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlement-cache.js","sourceRoot":"","sources":["../../lib/entitlement-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed error hierarchy for the Codex plugin.
|
|
3
|
+
* Provides structured error types with codes, causes, and context.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Error codes for categorizing errors.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ErrorCode: {
|
|
9
|
+
readonly NETWORK_ERROR: "CODEX_NETWORK_ERROR";
|
|
10
|
+
readonly API_ERROR: "CODEX_API_ERROR";
|
|
11
|
+
readonly AUTH_ERROR: "CODEX_AUTH_ERROR";
|
|
12
|
+
readonly VALIDATION_ERROR: "CODEX_VALIDATION_ERROR";
|
|
13
|
+
readonly RATE_LIMIT: "CODEX_RATE_LIMIT";
|
|
14
|
+
readonly TIMEOUT: "CODEX_TIMEOUT";
|
|
15
|
+
};
|
|
16
|
+
export type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
17
|
+
/**
|
|
18
|
+
* Options for creating a CodexError.
|
|
19
|
+
*/
|
|
20
|
+
export interface CodexErrorOptions {
|
|
21
|
+
code?: string;
|
|
22
|
+
cause?: unknown;
|
|
23
|
+
context?: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Base error class for all Codex plugin errors.
|
|
27
|
+
* Supports error chaining via `cause` and arbitrary context data.
|
|
28
|
+
*/
|
|
29
|
+
export declare class CodexError extends Error {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly code: string;
|
|
32
|
+
readonly context?: Record<string, unknown>;
|
|
33
|
+
constructor(message: string, options?: CodexErrorOptions);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Options for creating a CodexApiError.
|
|
37
|
+
*/
|
|
38
|
+
export interface CodexApiErrorOptions extends CodexErrorOptions {
|
|
39
|
+
status: number;
|
|
40
|
+
headers?: Record<string, string>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Error for HTTP/API response errors.
|
|
44
|
+
*/
|
|
45
|
+
export declare class CodexApiError extends CodexError {
|
|
46
|
+
readonly name = "CodexApiError";
|
|
47
|
+
readonly status: number;
|
|
48
|
+
readonly headers?: Record<string, string>;
|
|
49
|
+
constructor(message: string, options: CodexApiErrorOptions);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Options for creating a CodexAuthError.
|
|
53
|
+
*/
|
|
54
|
+
export interface CodexAuthErrorOptions extends CodexErrorOptions {
|
|
55
|
+
accountId?: string;
|
|
56
|
+
retryable?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Error for authentication failures.
|
|
60
|
+
*/
|
|
61
|
+
export declare class CodexAuthError extends CodexError {
|
|
62
|
+
readonly name = "CodexAuthError";
|
|
63
|
+
readonly accountId?: string;
|
|
64
|
+
readonly retryable: boolean;
|
|
65
|
+
constructor(message: string, options?: CodexAuthErrorOptions);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Options for creating a CodexNetworkError.
|
|
69
|
+
*/
|
|
70
|
+
export interface CodexNetworkErrorOptions extends CodexErrorOptions {
|
|
71
|
+
retryable?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Error for network/connection failures.
|
|
75
|
+
*/
|
|
76
|
+
export declare class CodexNetworkError extends CodexError {
|
|
77
|
+
readonly name = "CodexNetworkError";
|
|
78
|
+
readonly retryable: boolean;
|
|
79
|
+
constructor(message: string, options?: CodexNetworkErrorOptions);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Options for creating a CodexValidationError.
|
|
83
|
+
*/
|
|
84
|
+
export interface CodexValidationErrorOptions extends CodexErrorOptions {
|
|
85
|
+
field?: string;
|
|
86
|
+
expected?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Error for input validation failures.
|
|
90
|
+
*/
|
|
91
|
+
export declare class CodexValidationError extends CodexError {
|
|
92
|
+
readonly name = "CodexValidationError";
|
|
93
|
+
readonly field?: string;
|
|
94
|
+
readonly expected?: string;
|
|
95
|
+
constructor(message: string, options?: CodexValidationErrorOptions);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Options for creating a CodexRateLimitError.
|
|
99
|
+
*/
|
|
100
|
+
export interface CodexRateLimitErrorOptions extends CodexErrorOptions {
|
|
101
|
+
retryAfterMs?: number;
|
|
102
|
+
accountId?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Error for rate limit exceeded.
|
|
106
|
+
*/
|
|
107
|
+
export declare class CodexRateLimitError extends CodexError {
|
|
108
|
+
readonly name = "CodexRateLimitError";
|
|
109
|
+
readonly retryAfterMs?: number;
|
|
110
|
+
readonly accountId?: string;
|
|
111
|
+
constructor(message: string, options?: CodexRateLimitErrorOptions);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../lib/errors.ts"],"names":[],"mappings":"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"}
|