diff --git a/openapi.yaml b/openapi.yaml index 3d91562c..a523baae 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3,7 +3,7 @@ openapi: 3.0.3 info: title: Agent Dashboard for Claude Code API - version: 2.8.5 + version: 2.8.7 description: HTTP API for real-time Claude Code session monitoring, agent lifecycle tracking, analytics, pricing, hooks ingestion, and workflow intelligence. contact: name: Nguyễn Ngọc Trí Vĩ @@ -13328,6 +13328,178 @@ paths: description: 'ENOBACKUPDIR: no backup folder is set' '403': description: 'EBADORIGIN: another site asked' + /api/settings/retention: + get: + tags: + - Settings + summary: Automatic retention and database space + operationId: getRetention + responses: + '200': + description: Automatic retention setting, database space and the last Compact + content: + application/json: + schema: + type: object + required: + - days + - max_days + - env_owned + - compacting + properties: + days: + type: integer + minimum: 0 + maximum: 3650 + description: Finished sessions older than this many days are purged by the hourly maintenance; 0 = off (the default) + max_days: + type: integer + env_owned: + type: object + properties: + days: + type: boolean + last_run: + type: object + nullable: true + description: 'This process''s last hourly maintenance: {at, retention?: {purged_sessions, purged_events, purged_agents, error?}, reclaim: {reclaimed_pages, error?}, ms}' + space: + type: object + nullable: true + properties: + auto_vacuum: + type: string + enum: + - none + - full + - incremental + page_size: + type: integer + page_count: + type: integer + freelist_pages: + type: integer + free_bytes: + type: integer + db_size: + type: integer + description: Database file size in bytes + compacting: + type: boolean + last_compact: + type: object + nullable: true + description: 'This process''s last Compact: {at, ok, size_before?, size_after?, ms?, error?}' + put: + tags: + - Settings + summary: Set the automatic retention + description: Persisted to the settings file as AGENTHUB_RETENTION_DAYS. 0 turns it off. Same-origin only; in server mode an admin's. + operationId: setRetention + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - days + properties: + days: + type: integer + minimum: 0 + maximum: 3650 + responses: + '200': + description: The new state + content: + application/json: + schema: + type: object + required: + - days + - max_days + - env_owned + - compacting + properties: + days: + type: integer + minimum: 0 + maximum: 3650 + description: Finished sessions older than this many days are purged by the hourly maintenance; 0 = off (the default) + max_days: + type: integer + env_owned: + type: object + properties: + days: + type: boolean + last_run: + type: object + nullable: true + description: 'This process''s last hourly maintenance: {at, retention?: {purged_sessions, purged_events, purged_agents, error?}, reclaim: {reclaimed_pages, error?}, ms}' + space: + type: object + nullable: true + properties: + auto_vacuum: + type: string + enum: + - none + - full + - incremental + page_size: + type: integer + page_count: + type: integer + freelist_pages: + type: integer + free_bytes: + type: integer + db_size: + type: integer + description: Database file size in bytes + compacting: + type: boolean + last_compact: + type: object + nullable: true + description: 'This process''s last Compact: {at, ok, size_before?, size_after?, ms?, error?}' + '400': + description: 'INVALID_INPUT: days is not a whole number from 0 to 3650' + '403': + description: 'EBADORIGIN: another site asked' + '409': + description: 'EENVOWNED: the environment set AGENTHUB_RETENTION_DAYS' + '500': + description: 'ESAVE: the settings file could not be written' + /api/settings/compact: + post: + tags: + - Settings + summary: Compact the database now (full VACUUM) + description: Rewrites the database in a worker thread, switching it to incremental auto_vacuum, then truncates the WAL. Hook writes wait while it runs. Answers at once; poll GET /api/settings/retention while `compacting`. Same-origin only; in server mode an admin's. + operationId: compactDatabase + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - confirm + properties: + confirm: + type: boolean + enum: + - true + responses: + '202': + description: Started (or already running); the retention state + '400': + description: 'ECONFIRM: confirm: true is missing' + '403': + description: 'EBADORIGIN: another site asked' /api/settings/codex-hooks: get: tags: diff --git a/server/__tests__/codex-usage.test.js b/server/__tests__/codex-usage.test.js index fb92a986..7dec3f82 100644 --- a/server/__tests__/codex-usage.test.js +++ b/server/__tests__/codex-usage.test.js @@ -179,7 +179,9 @@ describe("ingestCodexUsage", () => { }); it("settles an active session to completed once its journals go quiet", async () => { - const now = Date.now(); + // A whole second: utimes rounds through a float, and a millisecond `now` + // came back from stat 1 ms early about half the time. + const now = Math.floor(Date.now() / 1000) * 1000; writeRollout( "s1", [meta("s1"), ctx("gpt-a"), tc([1, 0, 1], [1, 0, 1])], diff --git a/server/__tests__/repo-links.test.js b/server/__tests__/repo-links.test.js index a5d7d39d..ed429f3f 100644 --- a/server/__tests__/repo-links.test.js +++ b/server/__tests__/repo-links.test.js @@ -50,6 +50,8 @@ function trackedFiles() { // Generated from server/openapi-extra/* (which IS linted): the drift test // below covers it, so linting both would only double-report one fix. .filter((f) => f !== "openapi.yaml" && !f.endsWith("package-lock.json")) + // Third-party files copied unchanged (server/vendor/redoc): their links are upstream's. + .filter((f) => !f.includes("/vendor/")) ); }