Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
51fd30e
feat(usage): add safe ledger retention compaction core
Vocllum Sep 8, 2026
5ef1ba4
test(usage): cover safe ledger retention boundaries
Vocllum Sep 8, 2026
5dd4e81
feat(usage): persist strict ledger retention policy
Vocllum Sep 8, 2026
5963d42
feat(usage): move ledger compaction preparation to worker
Vocllum Sep 8, 2026
3a7e126
feat(usage): add background ledger retention job
Vocllum Sep 8, 2026
94cfeb3
feat(usage): schedule background ledger retention checks
Vocllum Sep 8, 2026
fb3f7bb
feat(usage): wire retention into server lifecycle
Vocllum Sep 8, 2026
b8ecf64
feat(storage): expose usage ledger retention controls
Vocllum Sep 8, 2026
590a80a
feat(storage): add usage history size limit panel
Vocllum Sep 8, 2026
e13fa24
feat(storage): surface usage retention in storage workspace
Vocllum Sep 8, 2026
ae1de71
test(usage): cover retention config and commit races
Vocllum Sep 8, 2026
5174875
fix(usage): preserve configured ceiling while retention is off
Vocllum Sep 8, 2026
b94e2f8
feat(storage): declare usage retention management routes
Vocllum Sep 8, 2026
96d52d4
feat(storage): add usage history limit CLI
Vocllum Sep 8, 2026
a492ecb
test(storage): cover usage history limit CLI
Vocllum Sep 8, 2026
d7fa8a9
feat(storage): declare usage history limit capability
Vocllum Sep 8, 2026
c5ec00a
fix(usage): join retention worker during abort
Vocllum Sep 8, 2026
df690d8
feat(storage): tag usage retention atomic replaces
Vocllum Sep 8, 2026
ffbf2f8
fix(usage): use Windows-tolerant atomic ledger replace
Vocllum Sep 8, 2026
e2c1e0d
fix(storage): keep policy save separate from immediate trim
Vocllum Sep 8, 2026
b5fea12
fix(usage): preserve exact row boundaries and owned candidates
Vocllum Sep 8, 2026
e796a64
fix(usage): pass parent-owned retention candidate path
Vocllum Sep 8, 2026
d1cd585
fix(usage): invalidate stale retention runs and clean candidates
Vocllum Sep 8, 2026
3352d90
fix(storage): invalidate retention work after policy changes
Vocllum Sep 8, 2026
2b6b10c
test(usage): cover exact retention row boundaries
Vocllum Sep 8, 2026
cc473b6
feat(gui): localize usage retention controls
Vocllum Sep 8, 2026
9878f3d
feat(gui): register usage retention translations
Vocllum Sep 8, 2026
955c4a7
fix(gui): route usage retention copy through i18n
Vocllum Sep 8, 2026
d2d587d
docs(cli): preserve storage safety rationale
Vocllum Sep 8, 2026
bc228b1
docs(usage): document retention scheduler helpers
Vocllum Sep 8, 2026
040d4fc
docs(server): document shared background lifecycle helpers
Vocllum Sep 8, 2026
2420126
fix(usage): discard stale history projection after retention
Vocllum Sep 8, 2026
e27e3fc
fix(usage): parameterize derived history cleanup
Vocllum Sep 8, 2026
a5edf91
fix(usage): reclaim derived history index after retention
Vocllum Sep 8, 2026
e8b1df8
test(usage): cover derived history cleanup ordering
Vocllum Sep 8, 2026
f06f1f3
fix(usage): preserve history db when sidecar cleanup is blocked
Vocllum Sep 8, 2026
47f7e45
fix(storage): gate retention apply on saved policy
Vocllum Sep 8, 2026
4b3f2a7
fix(storage): prevent applying unsaved usage limit
Vocllum Sep 8, 2026
00aa097
test: cover usage retention replace publisher
Vocllum Sep 8, 2026
050b00a
test(usage): cover stale retention policy generation
Vocllum Sep 8, 2026
ede183e
fix(config): type and validate usage ledger retention
Vocllum Sep 8, 2026
71694ef
docs(cli): document usage ledger retention surface
Vocllum Sep 8, 2026
e100054
fix(gui): localize retention units and type fallback
Vocllum Sep 8, 2026
2776903
test(gui): separate base and overlay locale contracts
Vocllum Sep 8, 2026
3bd1c54
feat(gui): compact usage retention controls
Vocllum Sep 8, 2026
f469735
fix(gui): label retention preset controls
Vocllum Sep 8, 2026
1e29707
fix(storage): reconcile retention route parity
Vocllum Sep 8, 2026
ed57484
fix(gui): avoid new retention lint suppression
Vocllum Sep 8, 2026
1edcc06
feat(usage): streamline ledger retention controls
Vocllum Sep 8, 2026
b22444f
docs(pr): add Usage retention preview
Vocllum Sep 8, 2026
27304bd
chore(gui): drop unused retention overlay plumbing
Vocllum Sep 8, 2026
bb62249
test(usage): cover Unlimited default
Vocllum Sep 8, 2026
3d67220
fix(usage): reflect custom retention draft
Vocllum Sep 8, 2026
3802922
refactor(usage): reduce retention UI to one toggle
Vocllum Sep 8, 2026
0cd5e0e
test(gui): exercise usage retention toggle
Vocllum Sep 8, 2026
8b60d22
docs(pr): remove synthetic usage preview
Vocllum Sep 8, 2026
97f3185
style(usage): match compact retention toggle
Vocllum Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ badge or the version value to read the full value.
| **Subagents** | Feature up to five bare native or namespaced routed models in the `spawn_agent` override list. |
| **Models** | Toggle native GPT and routed models, set provider allowlists and context caps, choose v1/base/v2, and configure the v2 thread limit. Configured providers stay visible as zero-model groups when discovery is off or returns no rows. |
| **Logs** | Auto-refresh recent requests with tokens, requested effort and (when available) effective outbound effort, resolved model, provider, status, request id, duration, and error details. The detail view includes the exact reasoning wire field when the adapter emits one. Filter by opaque conversation/session id (when the client sends one) to total tokens and estimated list-price cost for the currently loaded Logs ring. |
| **Usage / Debug** | Inspect token-usage coverage and trends, or enable opt-in provider transport and usage-extraction diagnostics. |
| **Usage / Debug** | Inspect token-usage coverage and trends. The **Usage** page also exposes the opt-in size ceiling for `usage.jsonl` (`usageLedgerRetention.enabled` / `maxBytes`); the background scheduler compacts older complete rows when the ceiling is exceeded. Enable provider transport and usage-extraction diagnostics here as needed. |
| **Storage** | Read-only CODEX_HOME disk breakdown (sessions, archives, DBs, attachments). Optional archived cleanup: preview the oldest N%, then quarantine to `CODEX_HOME/.trash` (default) or permanently delete behind an explicit checkbox. **Auto-cleanup policy** is opt-in and **default OFF** (`storageCleanupPolicy.enabled`); configure threshold/target/schedule/mode on the Storage page, or trigger **Run now**. Quarantined entries can be restored from the Storage page (JSONL + threads). Active sessions stay read-only. Cleanup and restore are refused while Codex holds the newest/active `state_*.sqlite` locked. |
| **Stop** | Gracefully stop the proxy and installed background service, restore native Codex, and exit (`POST /api/stop`). On Windows with the Task Scheduler backend the dashboard refuses and asks you to run `ocx stop` instead: that wrapper can respawn the proxy after the task ends, and only a stop running outside this process can verify the restart window before restoring your client config. Nothing is changed when it refuses. |

Expand Down
15 changes: 15 additions & 0 deletions docs-site/src/content/docs/reference/cli/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,21 @@ separately, and requests with no matching price row are counted as
ocx usage --range today --provider xai
```

### `ocx storage usage-limit`

Inspect or change the opt-in `usage.jsonl` size ceiling. The setting is also available in the
dashboard on the **Usage** page.

```bash
ocx storage usage-limit show --json
ocx storage usage-limit set --enabled true --mib 512 --json
```

`set` sends only the fields supplied, so changing `--mib` preserves the saved enabled state.
The minimum ceiling is 1 MiB. A bare `usage-limit` invocation is read-only. The background
scheduler compacts complete JSONL rows after the ledger exceeds the configured ceiling. The
command drives `GET`/`PUT /api/storage/usage-ledger-retention` on the running proxy.

### `ocx debug <provider|usage|injection|claude> <on|off|status|reset|logs [-f]>`

Read or change runtime debug overrides through the running proxy's management API.
Expand Down
31 changes: 31 additions & 0 deletions docs-site/src/content/docs/reference/configuration/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ runs helper features around provider requests.
| `corsAllowOrigins?` | `string[]` | `[]` | Additional exact origins allowed by CORS. Loopback origins are always allowed. Authority-based browser extension origins such as `chrome-extension://<extension-id>` are supported; `*` is not a wildcard. Firefox and Safari regenerate the extension UUID (per install / per browser launch), so update the entry when the origin changes. |
| `apiKeys?` | `OcxApiKey[]` | `[]` | Generated `ocx_…` credentials accepted by management and data-plane auth on non-loopback binds. Dashboard-managed. |
| `storageCleanupPolicy?` | `StorageCleanupPolicy` | disabled | Opt-in archived-session cleanup policy. Never enabled implicitly. |
| `usageLedgerRetention?` | `UsageLedgerRetentionConfig` | disabled | Opt-in cap for the append-only `usage.jsonl` usage ledger. When enabled, the background scheduler compacts complete rows after the ledger exceeds `maxBytes`. |
| `appOwnedMemoryBudgetMb?` | `number` | `256` | Cap in MiB for evictable app-owned logs, caches, blobs, and continuation payloads. Range 64–4096; not an RSS cap. |
| `codexAutoStart?` | `boolean` | `true` | Let the Codex shim run `ocx ensure` before launching Codex. False makes ensure a no-op. |
| `codexShimAutoRestore?` | `boolean` | `true` | Restore an installed shim after a completed external Codex update replaces it. Environment opt-out: `OPENCODEX_CODEX_SHIM_AUTO_RESTORE=0`. |
Expand Down Expand Up @@ -221,6 +222,36 @@ either `target.reduceToBytes` or `target.removeOldestPercent`. `mode` defaults t
Configure it on the Storage page or with `GET`/`PUT /api/storage/cleanup-policy`; trigger a manual run
with `POST /api/storage/cleanup-policy/run`.

## Usage-ledger retention

`usageLedgerRetention` is disabled by default. It is an opt-in size ceiling for
`$OPENCODEX_HOME/usage.jsonl`, the append-only usage ledger used by the Usage page and
`GET /api/usage`. Enabling it lets the proxy compact older rows in a background Worker once
the file exceeds the configured limit; normal request handling is not blocked by the scan.

```json
{
"usageLedgerRetention": {
"enabled": false
}
}
```

The default is **Unlimited** (`enabled: false`). To enable a ceiling, set `enabled: true` together
with `maxBytes`; it must be a safe integer of at least 1 MiB (`1048576`). A saved ceiling is
retained when `enabled` is set to `false`, so an operator can pause retention without losing the
selected limit. Unknown keys and malformed values fail closed and leave retention disabled.

Compaction publishes a complete JSONL-row candidate only after the source revision and active-turn
checks still match. An unterminated crash tail is discarded; a single row larger than the ceiling
is dropped so the published ledger remains bounded. The derived request-history projection is
recreated after a successful publish. A policy change invalidates an in-flight candidate, and a
source append during scanning defers the commit for a later run.

The dashboard exposes this control on the **Usage** page. For headless operation, use
`ocx storage usage-limit` or the `GET`/`PUT` management routes below. Setting the policy is
non-destructive; the background scheduler compacts older rows when the ceiling is exceeded.

## Quota-reset notifications (`quotaResetNotify`)

Off by default. When the section is absent, no detection runs, no timer starts, and no state
Expand Down
9 changes: 9 additions & 0 deletions docs-site/src/content/docs/reference/management-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ by the current window size.
| `GET /api/claude/inbound-debug` | Read Claude inbound debug state and entries | — |
| `GET /api/usage` | Stream the complete usage ledger into compact aggregates, then incrementally fold verified appends; summarize by preset or inclusive custom window and client surface, with a Codex `accounts` breakdown keyed by stable non-PII log labels | 400 invalid custom bounds; returns an `error: "read_failed"` summary if storage cannot be read |
| `GET /api/storage` | Scan Codex storage usage by bucket | Returns an `error: "scan_failed"` payload on scan failure |
| `GET /api/storage/usage-ledger-retention` | Read the usage-ledger retention policy, current `usage.jsonl` size, over-limit state, and the last background job state | — |
| `PUT /api/storage/usage-ledger-retention` | Replace the retention fields supplied in `{ "enabled"?: boolean, "maxBytes"?: integer }`; omitted fields keep their saved values | 400 malformed body, unknown field, or `maxBytes` below 1 MiB; 500 `config_write_failed` |
| `POST /api/storage/cleanup/preview` | Preview archived-session cleanup and return a binding digest | 400 `invalid_json` or `invalid_percent` |
| `POST /api/storage/cleanup` | Quarantine or permanently remove the previewed archived set | 400 invalid input; 409 stale/busy/referenced state; 500 filesystem/database failure |
| `GET /api/storage/trash` | List quarantined cleanup entries | 500 `trash_list_failed` |
Expand All @@ -193,6 +195,13 @@ by the current window size.
| `POST /api/storage/cleanup-policy/run` | Start a manual cleanup-policy run | 409 `already_running`; 500 `cleanup_failed` |
| `GET /api/storage/cleanup-policy/test-stream` | Test-only policy stream hook | 404 `not_found` when unavailable |

The retention status response is shaped as `{ enabled, maxBytes, currentBytes, overLimit, job }`;
`job` reports the process-local background state (`idle` or `running`) and the last outcome when
one exists. `PUT` accepts only `enabled` and `maxBytes`, and merges the supplied fields with the
saved policy. It never starts a compaction by itself. The background scheduler queues a Worker
when the ledger exceeds the ceiling; the canonical ledger is replaced only after complete-row,
active-turn, and source-revision checks pass.

New xAI attempts in `usage.jsonl` include a request-time `credentialSource`: `grok-oauth`
for the resolved Grok CLI OAuth transport, or `xai-api-key` for the public xAI API key
transport. This fixed label contains no credential or account identifier. It belongs to
Expand Down
109 changes: 109 additions & 0 deletions gui/src/components/usage/UsageLedgerRetentionControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { useCallback, useEffect, useState } from "react";
import { formatBytes } from "../../format-bytes";
import { useI18n } from "../../i18n/shared";
import { Switch } from "../../ui";

interface RetentionStatus {
enabled: boolean;
maxBytes: number;
currentBytes?: number;
}

function parseStatus(value: unknown): RetentionStatus {
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("invalid_status");
const candidate = value as Record<string, unknown>;
if (typeof candidate.enabled !== "boolean" || typeof candidate.maxBytes !== "number"
|| !Number.isFinite(candidate.maxBytes) || candidate.maxBytes <= 0) {
throw new Error("invalid_status");
}
return {
enabled: candidate.enabled,
maxBytes: candidate.maxBytes,
currentBytes: typeof candidate.currentBytes === "number" && Number.isFinite(candidate.currentBytes)
? candidate.currentBytes
: undefined,
};
}

/**
* Minimal Usage-page toggle for the opt-in usage-ledger byte ceiling.
*
* The concrete ceiling remains an API/CLI setting. The dashboard only enables or
* disables the exact value already reported by the server, so a non-MiB-aligned
* value can never be rounded or silently rewritten by the UI.
*/
export default function UsageLedgerRetentionControl({ apiBase }: { apiBase: string }) {
const { locale, t } = useI18n();
const [status, setStatus] = useState<RetentionStatus | null>(null);
const [busy, setBusy] = useState(false);
const [error, setError] = useState<string | null>(null);

const load = useCallback(async (signal?: AbortSignal) => {
const response = await fetch(`${apiBase}/api/storage/usage-ledger-retention`, { signal });
if (!response.ok) throw new Error("load_failed");
const next = parseStatus(await response.json());
if (signal?.aborted) return;
setStatus(next);
}, [apiBase]);

useEffect(() => {
const controller = new AbortController();
const timeout = window.setTimeout(() => {
void load(controller.signal).catch(errorValue => {
if (!controller.signal.aborted && (errorValue as { name?: string })?.name !== "AbortError") {
setError(t("usage.retention.error"));
}
});
}, 0);
return () => {
window.clearTimeout(timeout);
controller.abort();
};
}, [load, t]);

const persist = useCallback(async (nextEnabled: boolean, maxBytes: number) => {
setBusy(true);
setError(null);
try {
const response = await fetch(`${apiBase}/api/storage/usage-ledger-retention`, {
method: "PUT",
headers: { "content-type": "application/json" },
body: JSON.stringify({ enabled: nextEnabled, maxBytes }),
});
if (!response.ok) throw new Error("save_failed");
setStatus(parseStatus(await response.json()));
} catch {
setError(t("usage.retention.error"));
} finally {
setBusy(false);
}
}, [apiBase, t]);

const toggle = () => {
if (!status || busy) return;
void persist(!status.enabled, status.maxBytes);
};

return (
<section className="usage-retention-control" data-testid="usage-ledger-retention" aria-labelledby="usage-retention-title">
<div className="usage-retention-heading">
<div>
<h3 id="usage-retention-title" className="h-section">{t("usage.retention.title")}</h3>
<p className="muted text-control">{t("usage.retention.help")}</p>
</div>
<Switch
on={status?.enabled === true}
onClick={toggle}
disabled={busy || status === null}
label={t("usage.retention.enabled")}
/>
</div>

<p className="muted text-caption usage-retention-current">
{t("usage.retention.current")}: {status?.currentBytes === undefined ? "—" : formatBytes(status.currentBytes, locale)}
{status ? ` · ${t("usage.retention.limit")}: ${formatBytes(status.maxBytes, locale)}` : ""}
</p>
{error && <p className="err" role="alert">{error}</p>}
</section>
);
}
37 changes: 20 additions & 17 deletions gui/src/i18n/catalogs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { en, type TKey } from "./en";
import { en, type TKey as BaseTKey } from "./en";
import { de } from "./de";
import { fr } from "./fr";
import { ko } from "./ko";
Expand All @@ -11,35 +11,38 @@ import { LAB_CATALOG_OVERRIDES, type LabLocale } from "./lab-translations";

/** React-free locale catalog registry for formatters and other shared helpers. */
export type Locale = LabLocale;
export type TKey = BaseTKey;

function withLabTranslations(locale: Locale, catalog: Record<TKey, string>): Record<TKey, string> {
return { ...catalog, ...LAB_CATALOG_OVERRIDES[locale] };
/** Apply the centrally maintained Lab closed-surface translations to one base locale catalog. */
function withCatalogOverlays(locale: Locale, catalog: Record<BaseTKey, string>): Record<TKey, string> {
return {
...catalog,
...LAB_CATALOG_OVERRIDES[locale],
};
}

/**
* CL-05 translations are overlaid centrally so the compatibility surface cannot regress to
* copied English values in a locale catalog. The locale parity test still validates the base
* catalogs; this overlay is deliberately limited to the closed `lab.*` namespace.
* Lab translations are overlaid centrally so the compatibility surface cannot regress to copied
* English values. Base locale parity remains compile-checked by the locale modules.
*/
export const DICTS: Record<Locale, Record<TKey, string>> = {
en: withLabTranslations("en", en),
de: withLabTranslations("de", de),
fr: withLabTranslations("fr", fr),
ko: withLabTranslations("ko", ko),
zh: withLabTranslations("zh", zh),
"zh-TW": withLabTranslations("zh-TW", zhTW),
ru: withLabTranslations("ru", ru),
ja: withLabTranslations("ja", ja),
tr: withLabTranslations("tr", tr),
en: withCatalogOverlays("en", en),
de: withCatalogOverlays("de", de),
fr: withCatalogOverlays("fr", fr),
ko: withCatalogOverlays("ko", ko),
zh: withCatalogOverlays("zh", zh),
"zh-TW": withCatalogOverlays("zh-TW", zhTW),
ru: withCatalogOverlays("ru", ru),
ja: withCatalogOverlays("ja", ja),
tr: withCatalogOverlays("tr", tr),
};

/** Native language names shown by the language picker, kept inside i18n rather than UI metadata. */
export function localeDisplayName(locale: Locale): string {
return DICTS[locale]["lang.nativeName"];
}

/** Read one localized string without requiring React context. */
export function catalogValue(locale: Locale, key: TKey): string {
return DICTS[locale][key];
}

export type { TKey };
8 changes: 8 additions & 0 deletions gui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,14 @@ export const de: Record<TKey, string> = {
"debug.noLines.usage": "Nutzungserfassung ist an, aber es wurde noch nichts erfasst. Sende einen Chat/eine Anfrage über Codex, dann erscheint es hier.",
"debug.noLines.injection": "Injektions-Log ist an, aber es wurde noch nichts erfasst. Es erfasst Multi-Agent-Guidance-Injektion und Effort-Cap-Entscheidungen bei Collab- und Sub-Agent-Turns.",
"usage.title": "Nutzung",
"usage.retention.title": "Größenlimit für Nutzungsverlauf",
"usage.retention.help": "Optional können die neuesten vollständigen Nutzungsdatensätze innerhalb eines Größenlimits behalten werden. Ältere Einträge werden automatisch entfernt, sobald der Verlauf das Limit überschreitet.",
"usage.retention.enabled": "Größe des Nutzungsverlaufs begrenzen",
"usage.retention.current": "Aktuelle Größe",
"usage.retention.limit": "Maximale Größe",
"usage.retention.unlimited": "Unbegrenzt",
"usage.retention.error": "Das Größenlimit für den Nutzungsverlauf konnte nicht aktualisiert werden.",
"usage.retention.disabled": "Unbegrenzt — automatische Verlaufskomprimierung ist deaktiviert.",
"usage.subtitle": "Lokale Token-Buchhaltung deines Proxys. Fehlende Nutzung wird nie als Null angezeigt.",
"usage.loading": "Lade Nutzungsdaten…",
"usage.empty": "Noch keine Nutzung erfasst. Sende eine Anfrage über den Proxy, um Aktivität hier zu sehen.",
Expand Down
8 changes: 8 additions & 0 deletions gui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,14 @@ export const en = {

// usage page
"usage.title": "Usage",
"usage.retention.title": "Usage history size limit",
"usage.retention.help": "Optionally keep the newest complete usage records within a size limit. Older rows are removed automatically when the ledger exceeds it.",
"usage.retention.enabled": "Limit usage history size",
"usage.retention.current": "Current size",
"usage.retention.limit": "Maximum size",
"usage.retention.unlimited": "Unlimited",
"usage.retention.error": "Could not update the usage history limit.",
"usage.retention.disabled": "Unlimited — automatic history compaction is off.",
"usage.subtitle": "Local token accounting from your proxy. Missing usage is never shown as zero.",
"usage.loading": "Loading usage data…",
"usage.empty": "No usage recorded yet. Send a request through the proxy to see activity here.",
Expand Down
8 changes: 8 additions & 0 deletions gui/src/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,14 @@ export const fr: Record<TKey, string> = {
"debug.noLines.usage": "L’extraction de l’utilisation est activée, mais rien n’a encore été capturé. Envoyez une conversation ou une requête par Codex pour qu’elle apparaisse ici.",
"debug.noLines.injection": "Le journal des injections est activé, mais rien n’a encore été capturé. Il consigne l’injection des directives multi-agents et les décisions de plafonnement du niveau lors des tours collab et des sous-agents.",
"usage.title": "Utilisation",
"usage.retention.title": "Limite de taille de l’historique d’utilisation",
"usage.retention.help": "Conservez facultativement les enregistrements d’utilisation complets les plus récents dans une limite de taille. Les lignes plus anciennes sont supprimées automatiquement lorsque l’historique la dépasse.",
"usage.retention.enabled": "Limiter la taille de l’historique d’utilisation",
"usage.retention.current": "Taille actuelle",
"usage.retention.limit": "Taille maximale",
"usage.retention.unlimited": "Illimitée",
"usage.retention.error": "Impossible de mettre à jour la limite de l’historique d’utilisation.",
"usage.retention.disabled": "Illimitée — la compression automatique de l’historique est désactivée.",
"usage.subtitle": "Comptabilisation locale des jetons par votre proxy. Une utilisation manquante n’est jamais affichée comme nulle.",
"usage.loading": "Chargement des données d’utilisation…",
"usage.empty": "Aucune utilisation enregistrée pour le moment. Envoyez une requête par le proxy pour voir l’activité ici.",
Expand Down
Loading
Loading