From 8656108046acb1ad7bae0b075014d2c4c09429dd Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 19:38:51 +0900 Subject: [PATCH 1/9] feat(dashboard): expose separate dictation and live voice controls --- .../021_streaming_checks.md | 39 +++++ .../030_connections.md | 57 +++++- .../docs/fr/reference/proxy-formats.md | 2 + .../docs/ja/reference/proxy-formats.md | 2 + .../docs/ko/reference/proxy-formats.md | 2 + .../content/docs/reference/proxy-formats.md | 11 ++ .../docs/ru/reference/proxy-formats.md | 2 + .../docs/tr/reference/proxy-formats.md | 2 + .../docs/zh-cn/reference/proxy-formats.md | 2 + .../docs/zh-tw/reference/proxy-formats.md | 2 + gui/src/api.ts | 10 ++ gui/src/audio-api-client.ts | 148 ++++++++++++++++ .../apikeys-workspace/ApiKeysWorkspace.tsx | 11 ++ .../apikeys-workspace/AudioApiPanel.tsx | 162 ++++++++++++++++++ gui/src/i18n/de.ts | 29 ++++ gui/src/i18n/en.ts | 29 ++++ gui/src/i18n/fr.ts | 29 ++++ gui/src/i18n/ja.ts | 29 ++++ gui/src/i18n/ko.ts | 29 ++++ gui/src/i18n/ru.ts | 29 ++++ gui/src/i18n/tr.ts | 29 ++++ gui/src/i18n/zh-TW.ts | 29 ++++ gui/src/i18n/zh.ts | 29 ++++ gui/src/pages/ApiKeys.tsx | 8 + gui/src/pages/api-keys-utils.ts | 39 +++++ gui/src/styles-apikeys-workspace.css | 57 ++++++ gui/tests/api-auth-memory.test.ts | 25 ++- gui/tests/audio-api-client.test.ts | 145 ++++++++++++++++ gui/tests/audio-api-panel.test.tsx | 161 +++++++++++++++++ src/server/management/api-access.ts | 37 ++++ structure/gui-and-management-api.md | 20 +++ tests/server/api-access-endpoints.test.ts | 36 +++- 32 files changed, 1233 insertions(+), 8 deletions(-) create mode 100644 devlog/_plan/260912_audio_apis_stack/021_streaming_checks.md create mode 100644 gui/src/audio-api-client.ts create mode 100644 gui/src/components/apikeys-workspace/AudioApiPanel.tsx create mode 100644 gui/tests/audio-api-client.test.ts create mode 100644 gui/tests/audio-api-panel.test.tsx diff --git a/devlog/_plan/260912_audio_apis_stack/021_streaming_checks.md b/devlog/_plan/260912_audio_apis_stack/021_streaming_checks.md new file mode 100644 index 0000000000..da208293c7 --- /dev/null +++ b/devlog/_plan/260912_audio_apis_stack/021_streaming_checks.md @@ -0,0 +1,39 @@ +# Streaming outcome and next layer + +Functional layer: PASS at `011f2dff5ca3667b88f090fe711c4b2c77efd190`, PR #4392 onto +`codex/audio-transcription` (`71e22d967ffafbd3492935c299623c9127eaf17b`). +Remote run [34687731903](https://github.com/lidge-jun/opencodex/actions/runs/34687731903) +tested merge `df1c26d1`. + +- Audio client: 11 passing cases, job 103537614446. +- File transcription: 24 passing cases, job 103537614468. +- Dictation/voice ingress and lifecycle: 11 passing cases, job 103537614422. +- Call bindings: four passing cases on Linux and macOS, job 103537614428. +- Gates: typecheck, 1,979 dashboard tests, privacy, skill surface, release syntax + and CLI smoke passed, job 103537614453. Dashboard build skipped for this layer. +- Independent inherited source reviews: Ramanujan closure PASS; Feynman final + PASS after native platform-key compatibility and neutral WebSocket accounting + corrections. No unresolved blocking finding in these bounded reviews. + +All checks above ran remotely. Local product tests, typecheck, build and install +were NOT RUN for this layer, per owner instruction. Pushes used `--no-verify`. +No real upstream audio or personal recording was used. + +## Separate baseline failures + +Linux test 2/4 (103537614443) and macOS 2/2 (103537614422) still report +`tests/codex-integration/codex-journal.test.ts:170` (failed versus skipped restore) +and `:528` (routing retained after compensated failed restore). These failures +were observed before the final audio changes. They are recorded, not included +in this feature's repair scope, following the owner's explicit decision. +An earlier run also showed the stale-process status assertion at +`tests/cli/cli-status-json.test.ts:893`; do not claim it repaired without evidence. +Bun batch crashes that recovered through CI singleton retries are not runtime-fix +evidence. Whole-run green and merge readiness are not claimed. + +## Next + +Proceed to wp3: configured endpoint metadata, separate Dictation and Live Voice +controls, synthetic browser QA of the CI-built dashboard, and ordinary stacked +publication. Configuration is not entitlement or observed connectivity. Leave +all PRs open; aggregate baseline failures remain a separate publication note. diff --git a/devlog/_plan/260912_audio_apis_stack/030_connections.md b/devlog/_plan/260912_audio_apis_stack/030_connections.md index ccb9fa24ce..e70417ddef 100644 --- a/devlog/_plan/260912_audio_apis_stack/030_connections.md +++ b/devlog/_plan/260912_audio_apis_stack/030_connections.md @@ -2,19 +2,30 @@ Depends on wp2 completed audio routes. The first two layers remain independently usable through external client examples. +P revalidation: wp2 D concluded functional audio tests and source reviews PASS at +011f2dff5c, with unrelated journal failures recorded separately. Follow that +direction without expanding baseline repairs. C4 because transient credentials +cross the browser/data-plane boundary. Main owns implementation; inherited +architect and independent reviewers remain read-only. No additional cost/time +budget was imposed. No paid calls, personal audio, service changes or merge. +Local product suites/typecheck/build/install are prohibited. Existing remote CI +runs the checks; browser QA reads its built artifact with synthetic routes only. + ## File changes | Operation | Path | Contract change | | --- | --- | --- | | MODIFY | src/server/management/api-access.ts | extend ApiAccessEndpoints with transcription, dictationStream, live and realtimeCalls URLs plus truthful capability metadata | -| MODIFY | src/server/management-api.ts | include audio metadata using existing management authentication | +| EXISTING | src/server/management/oauth-account-routes.ts | /api/keys already serializes ...endpoints; no new route or management authority | | MODIFY | tests/server/api-access-endpoints.test.ts | URL host/protocol and capability projection tests | | MODIFY | gui/src/pages/api-keys-utils.ts | extend endpoint type/default/derive chain for new endpoints | | MODIFY | gui/src/pages/ApiKeys.tsx | consume serialized endpoint metadata through KeysResponse, CachedKeysShape, cache validation and fetchKeys | | MODIFY | gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx | place two unframed audio sections in existing Connections/API layout | | NEW | gui/src/components/apikeys-workspace/AudioApiPanel.tsx | accessible Dictation and Live Voice controls, endpoint/model display, sample copying, transient key/file controls and result/error states | | NEW | gui/src/audio-api-client.ts | bounded cancelable upload and socket client protocol helpers; no saved secrets | -| NEW | gui/tests/audio-api-client.test.ts | request generation, cancellation and transcript assembly tests | +| MODIFY | gui/src/api.ts | narrow audio upload fetch entry bypasses management auth injection/recovery, validates exact inference path | +| NEW | gui/tests/audio-api-client.test.ts | request generation, cancellation and protocol readiness tests | +| NEW | gui/tests/audio-api-panel.test.tsx | real component upload, error, cancellation, missing metadata and deactivation flows | | MODIFY | gui/src/i18n/en.ts and every locale module | complete localized label/status/action keys | | MODIFY | gui/src/styles-apikeys-workspace.css | restrained aligned responsive audio sections using existing tokens | | MODIFY | structure/gui-and-management-api.md | metadata and control ownership/current contract | @@ -26,11 +37,23 @@ Before: ApiAccessEndpoints contains Responses/chat/messages/models only. After: Metadata fields complete chain: creation buildApiAccessEndpoints -> JSON management response -> API page validation/mapping -> ApiEndpointInfo/AudioApiPanel. Defaults cannot claim configured availability. No new provider is registered and no audio model enters a text completion test. +The optional `audio` projection has transcriptionEndpoint, dictationStreamEndpoint, +liveEndpoint, realtimeCallsEndpoint, transcriptionModel, liveModel, +transcriptionConfigured, dictationConfigured and liveConfigured. URL/model strings +and booleans are validated on both network and cached payloads; endpoint origins +and paths must match the published base after WS-to-HTTP normalization. Missing +or invalid audio metadata leaves the audio controls unavailable, while existing +key management keeps working. Configuration flags use canonical enabled provider +configuration only, never credential resolution, account reads or a network probe. +File transcription accepts canonical ChatGPT or configured OpenAI API routing; +dictation/live GPT-Live flags require canonical ChatGPT routing (an API-key-only +configuration does not prove access to the Codex live model). + Dictation section contains model and endpoint copy actions, file input, transient API key input, transcribe/cancel, text result/copy and clear error states. Stream example names extension protocol and gives start/audio/close events. Live Voice section contains actual GPT-Live model and both WS/WebRTC connection endpoints, transient client key, a connect/disconnect test with status and observed event output. Browser WebSocket auth must use a short-lived local session mechanism or supported client protocol carrier; never expose ChatGPT credentials or persist raw keys. Do not create a fake success check or billable background probe. All test actions require a deliberate user click. UI is unframed and follows existing workspace colors/type/spacing. Icons reuse gui icons, all visible text is localized. At desktop and mobile widths long endpoint text wraps or scrolls within its own element without overlapping controls. Buttons have stable dimensions and stateful controls are keyboard reachable. -Pass the existing active flag through ApiKeysWorkspace. Integrations hides panels without unmounting; requests, sockets and timers must stop on deactivation as well as unmount. Existing key rows contain only prefixes: controls use a newly generated key or an explicitly entered transient key, never pretend a key ID can authenticate. Browser voice connection uses an OpenCodex-only WebSocket protocol credential carrier accepted solely by the audio routes; exact supported carrier and precedence are documented/tested in wp2. No persistent key or query authentication. +Pass the existing active flag through ApiKeysWorkspace. Integrations hides panels without unmounting; conditionally unmount just audio controls when inactive, preserving the rest of the workspace drafts. Requests, sockets and timers stop on deactivation, origin change and unmount. Existing key rows contain only prefixes: controls use an explicitly entered transient key, never pretend a key ID can authenticate. Key edits cancel pending work. Browser voice connection uses the OpenCodex-only WebSocket protocol credential carrier accepted solely by the audio routes; exact supported carrier and precedence are documented/tested in wp2. No persistent key or query authentication. Raw upstream messages are not rendered: show localized error categories and allowlisted event types only. Socket open is not success: wait for session.started/session.updated with a nonempty session.id. Probe sends no audio and closes after a bounded interval or explicit disconnect. ## Acceptance and publication @@ -38,7 +61,33 @@ Pass the existing active flag through ApiKeysWorkspace. Integrations hides panel 2. Mocked browser flow uploads a fixture, receives text, copies it, cancels a pending call and displays a server error. No real audio/provider requests during agent QA. 3. Mocked voice flow connects, observes a protocol event, disconnects and releases callbacks/timers; API keys never enter storage, screenshots or URL queries. 4. Desktop and mobile browser screenshots are read back and corrected. Screenshot attached to UI PR with synthetic data only. -5. Run GUI focused tests, lint:i18n, lint, build and repository typecheck/full suite before review ready; per-layer CI uses exact PR head. The final PRs fill Summary, Verification and Checklist plus ordinary stack map. +5. Remote CI executes dashboard tests/lint/build and repository typecheck/suite; no local execution. Inspect exact-head logs and download its dashboard-preview artifact. The final PRs fill Summary, Verification and Checklist plus ordinary stack map. Existing unrelated CI failures stay separately documented; never attest whole-suite green. + +Conditional acceptance includes empty/malformed metadata (disabled controls, no +request), file >25,000,000 bytes (local rejection before fetch), 401/429/503 +(localized categories, no raw body), aborted upload (no stale text), WS open +without ready event (timeout), protocol error (failed, not connected), key/origin +change and inactive/unmount (all resources closed, no late callback). Copy samples +contain placeholders only, never the transient input. Metadata tests cover TLS, +wildcard, IPv6 and loopback companion URLs. No new enforcement layer is claimed; +browser guards are early UX validation, and server admission remains authoritative. + +## P/A review disposition + +Accepted architect CONN-META-01, WIRE-02, URL-03, UI-04 and LIFE-06. Folded +CONN-PROBE-05 and the independent A review's three residuals: + +- On socket open send exactly `{"type":"session.update","session":{"instructions":"","audio":{"output":{"voice":"cove"}},"delegation":{"type":"client"}}}`. + Acknowledgments with closed/error/failed session status are terminal, never + ready. An error followed by normal close remains failed. +- The API module exposes a narrow raw audio-upload entry. It validates the exact + HTTP(S) `/v1/audio/transcriptions` destination and bypasses installed management + authentication and 401 recovery. Connected-mode tests install that wrapper and + assert the typed data key survives, with no session/CSRF/machine credentials. +- Endpoint validation rejects userinfo, query, fragment and incorrect schemes as + well as wrong origins/paths. Network and cache paths use the same validator. + Only the wp2 `opencodex-audio` / `opencodex-key.` carrier is used; + observed output means allowlisted event types, not raw messages. Commands are defined by root/gui package.json. Source paths and existing stylesheet/fetch owner are revalidated at this cycle P before implementation; any renamed path is amended with exact ownership evidence. No disconnected metadata fields or fake audio model tests are acceptable. diff --git a/docs-site/src/content/docs/fr/reference/proxy-formats.md b/docs-site/src/content/docs/fr/reference/proxy-formats.md index 3c6d010973..ee8e9dbce4 100644 --- a/docs-site/src/content/docs/fr/reference/proxy-formats.md +++ b/docs-site/src/content/docs/fr/reference/proxy-formats.md @@ -278,6 +278,8 @@ Voir [le guide Desktop](/fr/guides/claude-code/). Relecture thinking et cache re La liaison de compte ci-dessous concerne les clients Codex natifs. Pour la dictée et GPT-Live avec une clé API externe, consultez la [spécification audio en anglais](/reference/proxy-formats/#streaming-dictation). +Connections > API keys propose deux sections, Dictée et Voix en direct. La clé de données reste uniquement en mémoire dans le formulaire. La dictée envoie le fichier choisi ; la vérification vocale attend une confirmation de session sans microphone. Une configuration présente ne garantit pas la connexion. + `POST /v1/live` accepte la surface de création d'appel ChatGPT/Codex App sans cadre. `POST /v1/realtime/calls` accepte la surface de création d'appel OpenAI Realtime. opencodex sélectionne un route OpenAI-family éligible, normalise la demande de création d'appel pour l'authentification en amont diff --git a/docs-site/src/content/docs/ja/reference/proxy-formats.md b/docs-site/src/content/docs/ja/reference/proxy-formats.md index 565321c19d..2220dca5fa 100644 --- a/docs-site/src/content/docs/ja/reference/proxy-formats.md +++ b/docs-site/src/content/docs/ja/reference/proxy-formats.md @@ -204,6 +204,8 @@ API ではありません。Desktop のキー移行・復旧・切断は既存 以下のアカウント連携は既存の Codex クライアント向けです。外部 API キーで利用する音声入力と GPT-Live は[英語版の音声 API 仕様](/reference/proxy-formats/#streaming-dictation)を参照してください。 +Connections > API keys に音声入力とリアルタイム音声の項目があります。データキーは入力欄のメモリにのみ保持されます。文字起こしは選択したファイルを送信し、音声の接続確認はマイクを使わずセッション応答を待ちます。設定済みの表示は接続成功を意味しません。 + `POST /v1/live` は、ChatGPT/Codex アプリのフレームレス通話作成サーフェスを受け入れます。 `POST /v1/realtime/calls` は、OpenAI Realtime 呼び出し作成サーフェスを受け入れます。 opencodex は、適格な OpenAI ファミリ ルートを選択し、アップストリーム認証モードのコール作成リクエストを正規化し、制限付き応答を中継します。 コールの作成後、クライアントはサポートされている受信フォームを使用してサイドバンド WebSocket に参加できます。 diff --git a/docs-site/src/content/docs/ko/reference/proxy-formats.md b/docs-site/src/content/docs/ko/reference/proxy-formats.md index 969ed6bf63..e4fe0befb9 100644 --- a/docs-site/src/content/docs/ko/reference/proxy-formats.md +++ b/docs-site/src/content/docs/ko/reference/proxy-formats.md @@ -251,6 +251,8 @@ HTTP 400을 반환합니다. 두 경우 모두 날짜 제거나 다른 경로로 아래 계정 연결 설명은 기존 Codex 클라이언트 기준입니다. 외부 API 키로 쓰는 받아쓰기와 GPT-Live는 [영문 음성 API 명세](/reference/proxy-formats/#streaming-dictation)를 따릅니다. +Connections > API keys에는 받아쓰기와 실시간 음성 블록이 있습니다. 데이터 키는 입력란에만 잠시 유지됩니다. 받아쓰기는 선택한 파일을 전송하고, 음성 연결 확인은 마이크 없이 세션 응답을 기다립니다. 설정 표시는 실제 연결 성공을 뜻하지 않습니다. + `POST /v1/live`는 ChatGPT/Codex App Frameless call-creation 표면을 받습니다. `POST /v1/realtime/calls`는 OpenAI Realtime call-creation 표면을 받습니다. opencodex는 적절한 OpenAI 계열 경로를 선택하고, 업스트림 인증 모드에 맞게 call-creation 요청을 정규화한 뒤, 제한된 응답을 릴레이합니다. diff --git a/docs-site/src/content/docs/reference/proxy-formats.md b/docs-site/src/content/docs/reference/proxy-formats.md index e62c6ec419..4fcc72f60a 100644 --- a/docs-site/src/content/docs/reference/proxy-formats.md +++ b/docs-site/src/content/docs/reference/proxy-formats.md @@ -40,6 +40,17 @@ Credential-bearing model, image, video, and search requests do not automatically ## File transcription +Connections > API keys has separate **Dictation** and **Live Voice** blocks. +Enter an OpenCodex data key, not a provider or management key. Dictation uploads +the file you select and offers cancellation and transcript copying. Live Voice's +**Check connection** opens a session without microphone access or audio frames, +waits for the provider's session acknowledgment, and disconnects after one minute +or when you leave the panel. The key remains only in that panel's memory. +**Configured, not verified** describes provider configuration, not account +health or entitlement. Use the explicit action to observe a result. Examples use +key placeholders and never include the entered secret. An older server without +audio metadata leaves these controls unavailable. + `POST /v1/audio/transcriptions` accepts an OpenCodex data-plane key in `Authorization: Bearer`, `x-opencodex-api-key`, or `x-api-key`, including on a local listener. An explicitly supplied invalid key is rejected. Upload one audio file diff --git a/docs-site/src/content/docs/ru/reference/proxy-formats.md b/docs-site/src/content/docs/ru/reference/proxy-formats.md index e99fdb46b5..7f8b1d68ad 100644 --- a/docs-site/src/content/docs/ru/reference/proxy-formats.md +++ b/docs-site/src/content/docs/ru/reference/proxy-formats.md @@ -262,6 +262,8 @@ HTTP 400. В обоих случаях дата не удаляется и др Привязка аккаунта ниже относится к штатным клиентам Codex. Для диктовки и GPT-Live с внешним API-ключом см. [спецификацию аудио API на английском](/reference/proxy-formats/#streaming-dictation). +В Connections > API keys есть отдельные разделы диктовки и голоса. Ключ данных остаётся только в памяти формы. Диктовка отправляет выбранный файл, а проверка голоса ждёт подтверждения сеанса без микрофона. Наличие конфигурации не означает успешное подключение. + `POST /v1/live` принимает surface Frameless call-creation из ChatGPT/Codex App. `POST /v1/realtime/calls` принимает surface call-creation OpenAI Realtime. opencodex выбирает подходящий маршрут семейства OpenAI, нормализует запрос call-creation под нужный режим diff --git a/docs-site/src/content/docs/tr/reference/proxy-formats.md b/docs-site/src/content/docs/tr/reference/proxy-formats.md index e62daa374f..a4ce5ade7b 100644 --- a/docs-site/src/content/docs/tr/reference/proxy-formats.md +++ b/docs-site/src/content/docs/tr/reference/proxy-formats.md @@ -283,6 +283,8 @@ Thinking yeniden gönderimi ve önbellek, ayrı [#3719](https://github.com/lidge Aşağıdaki hesap bağlantısı yerel Codex istemcileri içindir. Harici API anahtarıyla dikte ve GPT-Live kullanımı için [İngilizce ses API belirtimine](/reference/proxy-formats/#streaming-dictation) bakın. +Connections > API keys altında Dikte ve Canlı Ses bölümleri bulunur. Veri anahtarı yalnızca form belleğinde tutulur. Dikte seçilen dosyayı gönderir; ses bağlantısı kontrolü mikrofon kullanmadan oturum onayını bekler. Yapılandırılmış olması bağlantının başarılı olduğu anlamına gelmez. + `POST /v1/live`, ChatGPT/Codex App Frameless çağrı oluşturma yüzeyini kabul eder. `POST /v1/realtime/calls`, OpenAI Realtime çağrı oluşturma yüzeyini kabul eder. opencodex uygun bir OpenAI ailesi rotası seçer, yukarı akış kimlik diff --git a/docs-site/src/content/docs/zh-cn/reference/proxy-formats.md b/docs-site/src/content/docs/zh-cn/reference/proxy-formats.md index 2c9e019b03..52e8237b48 100644 --- a/docs-site/src/content/docs/zh-cn/reference/proxy-formats.md +++ b/docs-site/src/content/docs/zh-cn/reference/proxy-formats.md @@ -215,6 +215,8 @@ thinking 重放与提示缓存仍由独立的 [#3719](https://github.com/lidge-j 下文的账户绑定说明适用于原生 Codex 客户端。通过外部 API 密钥使用语音转写和 GPT-Live,请参阅[英文音频 API 规范](/reference/proxy-formats/#streaming-dictation)。 +Connections > API keys 包含独立的听写和实时语音区域。数据密钥仅保留在表单内存中。听写会上传所选文件;语音连接检查不使用麦克风,而是等待会话确认。已配置不代表连接成功。 + `POST /v1/live` 接受 ChatGPT/Codex App 的 Frameless call-creation 表面。 `POST /v1/realtime/calls` 接受 OpenAI Realtime 的 call-creation 表面。opencodex 会选择 一个符合条件的 OpenAI 家族路由,将 call-creation 请求规范化为上游认证模式,并转发有界响应。 diff --git a/docs-site/src/content/docs/zh-tw/reference/proxy-formats.md b/docs-site/src/content/docs/zh-tw/reference/proxy-formats.md index 5c1672662e..b53e740cd8 100644 --- a/docs-site/src/content/docs/zh-tw/reference/proxy-formats.md +++ b/docs-site/src/content/docs/zh-tw/reference/proxy-formats.md @@ -193,6 +193,8 @@ thinking 重播與提示快取仍由獨立的 [#3719](https://github.com/lidge-j 下方帳戶綁定說明適用於原生 Codex 用戶端。透過外部 API 金鑰使用語音轉寫及 GPT-Live,請參閱[英文音訊 API 規格](/reference/proxy-formats/#streaming-dictation)。 +Connections > API keys 包含獨立的聽寫與即時語音區域。資料金鑰僅保留在表單記憶體中。聽寫會上傳選取的檔案;語音連線檢查不使用麥克風,而是等待工作階段確認。已設定不代表連線成功。 + `POST /v1/live` 接受 ChatGPT/Codex App Frameless call-creation 介面。 `POST /v1/realtime/calls` 接受 OpenAI Realtime call-creation 介面。opencodex 選擇一個合格的 OpenAI 家族路由、為上游認證模式正規化 call-creation 請求,並中繼有界的回應。 diff --git a/gui/src/api.ts b/gui/src/api.ts index 020183dd47..644c5f5610 100644 --- a/gui/src/api.ts +++ b/gui/src/api.ts @@ -340,6 +340,16 @@ export function installApiAuthFetch(): void { }; } +/** Audio is data-plane traffic, even when the connected management target is a relay. */ +export function fetchAudioUpload(endpoint: string, init: RequestInit): Promise { + const url = new URL(endpoint); + if (!["http:", "https:"].includes(url.protocol) || url.pathname !== "/v1/audio/transcriptions" + || url.username || url.password || url.search || url.hash || init.method !== "POST") { + return Promise.reject(new Error("Invalid audio upload destination")); + } + return (rawFetch ?? fetch)(url.href, { ...init, credentials: "omit", redirect: "error" }); +} + export function resetApiAuthFetchForTests(adminTokenPrompt: AdminTokenPrompt = promptForAdminToken): void { installed = false; rawFetch = null; diff --git a/gui/src/audio-api-client.ts b/gui/src/audio-api-client.ts new file mode 100644 index 0000000000..960fbf1841 --- /dev/null +++ b/gui/src/audio-api-client.ts @@ -0,0 +1,148 @@ +import { fetchAudioUpload } from "./api"; +import { createBoundedFetch } from "./bounded-fetch"; + +export type AudioErrorCode = "auth" | "unavailable" | "rateLimit" | "invalid" | "size" | "network" | "timeout" | "protocol"; +export class AudioApiError extends Error { + constructor(readonly code: AudioErrorCode) { super(code); } +} + +export const AUDIO_FILE_MAX_BYTES = 25_000_000; +const RESPONSE_MAX_BYTES = 2 * 1024 * 1024; + +export async function transcribeAudio(endpoint: string, model: string, key: string, file: File, signal: AbortSignal): Promise { + if (!file.size || file.size > AUDIO_FILE_MAX_BYTES) throw new AudioApiError("size"); + if (!key.trim()) throw new AudioApiError("auth"); + signal.throwIfAborted(); + const bounded = createBoundedFetch(130_000); + const abort = () => bounded.controller.abort(signal.reason); + signal.addEventListener("abort", abort, { once: true }); + try { + const form = new FormData(); + form.set("file", file); + form.set("model", model); + form.set("response_format", "json"); + const response = await fetchAudioUpload(endpoint, { + method: "POST", headers: { "X-OpenCodex-API-Key": key.trim() }, body: form, signal: bounded.signal, + }); + if (!response.ok) { + void response.body?.cancel().catch(() => {}); + throw new AudioApiError(response.status === 401 || response.status === 403 ? "auth" + : response.status === 429 ? "rateLimit" : response.status === 413 ? "size" + : response.status >= 500 ? "unavailable" : "invalid"); + } + const reader = response.body?.getReader(); + if (!reader) throw new AudioApiError("protocol"); + const cancel = () => { void reader.cancel().catch(() => {}); }; + bounded.signal.addEventListener("abort", cancel, { once: true }); + let text = ""; + let bytes = 0; + const decoder = new TextDecoder(); + try { + bounded.signal.throwIfAborted(); + for (;;) { + const part = await reader.read(); + bounded.signal.throwIfAborted(); + if (part.done) break; + bytes += part.value.byteLength; + if (bytes > RESPONSE_MAX_BYTES) throw new AudioApiError("protocol"); + text += decoder.decode(part.value, { stream: true }); + } + text += decoder.decode(); + } finally { + bounded.signal.removeEventListener("abort", cancel); + void reader.cancel().catch(() => {}); + reader.releaseLock(); + } + let data: unknown; + try { data = JSON.parse(text); } catch { throw new AudioApiError("protocol"); } + if (!data || typeof data !== "object" || typeof (data as { text?: unknown }).text !== "string") throw new AudioApiError("protocol"); + return (data as { text: string }).text; + } catch (error) { + if (signal.aborted) throw signal.reason; + if (bounded.signal.aborted) throw new AudioApiError("timeout"); + throw error instanceof AudioApiError ? error : new AudioApiError("network"); + } finally { + signal.removeEventListener("abort", abort); + bounded.clear(); + } +} + +export function audioSocketProtocols(key: string): string[] { + const bytes = new TextEncoder().encode(key.trim()); + if (!bytes.length || bytes.length > 4096) throw new AudioApiError("auth"); + let binary = ""; + for (const byte of bytes) binary += String.fromCharCode(byte); + const encoded = btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); + return ["opencodex-audio", `opencodex-key.${encoded}`]; +} + +export const LIVE_SESSION_UPDATE = { + type: "session.update", + session: { instructions: "", audio: { output: { voice: "cove" } }, delegation: { type: "client" } }, +} as const; +export type LiveAudioState = "connecting" | "connected" | "disconnected" | "failed"; +const DISPLAY_EVENTS = new Set(["session.started", "session.updated", "delegation.created", "output_audio.delta"]); + +/** Connection-only probe: no microphone, audio frame, delegation execution or reconnect. */ +export function connectLiveAudio(options: { + endpoint: string; model: string; key: string; + onState: (state: LiveAudioState, error?: AudioErrorCode) => void; + onEvent: (type: string) => void; + readyTimeoutMs?: number; + maxSessionMs?: number; +}): () => void { + const url = new URL(options.endpoint); + if (!["ws:", "wss:"].includes(url.protocol) || url.pathname !== "/v1/live" + || url.username || url.password || url.search || url.hash) throw new AudioApiError("invalid"); + url.searchParams.set("model", options.model); + const socket = new WebSocket(url.href, audioSocketProtocols(options.key)); + let finished = false; + let ready = false; + let lifetime: ReturnType | undefined; + const deadline = setTimeout(() => finish("failed", "timeout"), options.readyTimeoutMs ?? 15_000); + const dispose = () => { + clearTimeout(deadline); + clearTimeout(lifetime); + socket.onopen = socket.onmessage = socket.onerror = socket.onclose = null; + try { + if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ type: "session.close" })); + socket.close(); + } catch { /* A connecting or already closed browser socket has nothing left to send. */ } + }; + function finish(state: LiveAudioState, error?: AudioErrorCode) { + if (finished) return; + finished = true; + dispose(); + options.onState(state, error); + } + socket.onopen = () => { + try { socket.send(JSON.stringify(LIVE_SESSION_UPDATE)); } + catch { finish("failed", "network"); } + }; + socket.onmessage = event => { + if (finished) return; + if (typeof event.data !== "string" || event.data.length > 64 * 1024) { finish("failed", "protocol"); return; } + let message: { type?: unknown; session?: { id?: unknown; status?: unknown } }; + try { message = JSON.parse(event.data); } catch { finish("failed", "protocol"); return; } + if (!message || typeof message !== "object" || typeof message.type !== "string") { finish("failed", "protocol"); return; } + if (message.type === "error" || message.type === "protocol.error") { finish("failed", "protocol"); return; } + if (message.type === "session.started" || message.type === "session.updated") { + const session = message.session; + if (session?.status === "closed" || session?.status === "error" || session?.status === "failed") { + finish(ready ? "disconnected" : "failed", ready ? undefined : "protocol"); + return; + } + if (!ready && typeof session?.id === "string" && session.id.trim()) { + ready = true; + clearTimeout(deadline); + lifetime = setTimeout(() => finish("disconnected"), options.maxSessionMs ?? 60_000); + options.onState("connected"); + } + } + if (DISPLAY_EVENTS.has(message.type)) options.onEvent(message.type); + }; + socket.onerror = () => finish("failed", "network"); + socket.onclose = event => finish(ready && event.code === 1000 ? "disconnected" : "failed", ready && event.code === 1000 ? undefined : "network"); + options.onState("connecting"); + return () => { if (!finished) { finished = true; dispose(); } }; +} diff --git a/gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx b/gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx index dba0e23591..df3bf1974a 100644 --- a/gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx +++ b/gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx @@ -24,11 +24,13 @@ import { } from "../../pages/api-keys-panels"; import ClientConfigPanel from "./ClientConfigPanel"; import ApiKeysListPanel from "./ApiKeysListPanel"; +import { DictationPanel, LiveVoicePanel } from "./AudioApiPanel"; export interface ApiKeysWorkspaceProps { keys: ApiKeyEntry[]; /** Management API origin the client-config panel fetches from. */ apiBase: string; + active?: boolean; /** Dataset-level. Absent means nothing is attributable yet — a different * statement from a key whose counters read zero. */ attributionSince?: string; @@ -78,6 +80,7 @@ export interface ApiKeysWorkspaceProps { export default function ApiKeysWorkspace({ keys, apiBase, + active = true, attributionSince, historyTruncated, authMatrix, @@ -164,6 +167,8 @@ export default function ApiKeysWorkspace({ { id: "keys", label: t("api.section.keys"), meta: keysLoading ? undefined : String(keys.length) }, { id: "connect", label: t("api.section.connect") }, { id: "endpoints", label: t("api.section.endpoints") }, + { id: "dictation", label: t("audio.dictation") }, + { id: "live-voice", label: t("audio.liveVoice") }, { id: "models", label: t("api.section.models"), meta: String(modelCount) }, { id: "examples", label: t("api.section.examples") }, ], [t, keys.length, keysLoading, modelCount]); @@ -484,6 +489,12 @@ export default function ApiKeysWorkspace({
+
+ {active && } +
+
+ {active && } +
ws.send(JSON.stringify(${JSON.stringify(start)})); +ws.onmessage = ({ data }) => console.log(JSON.parse(data).type); +// After session.started, send your protocol's audio frames. +// Dictation: mono PCM16 at the declared sample_rate_hz (48000 here). +// Live Voice: input_audio.append / output_audio.delta, 24 kHz mono. +const closeSession = () => ws.send(JSON.stringify({ type: "session.close" }));`; +} + +function AudioKeyInput({ value, onChange }: { value: string; onChange: (value: string) => void }) { + const t = useT(); + const id = useId(); + return ; +} + +function AudioError({ code }: { code: AudioErrorCode | null }) { + const t = useT(); + return code ?

{t(`audio.error.${code}`)}

: null; +} + +export function DictationPanel({ audio }: { audio?: AudioApiInfo }) { + const t = useT(); + const [key, setKey] = useState(""); + const [file, setFile] = useState(null); + const [pending, setPending] = useState(false); + const [text, setText] = useState(null); + const [error, setError] = useState(null); + const request = useRef(null); + const fileId = useId(); + const titleId = useId(); + useEffect(() => () => { request.current?.abort(); request.current = null; }, []); + const cancel = () => { + request.current?.abort(); + request.current = null; + setPending(false); + }; + const upload = async () => { + if (!audio?.transcriptionConfigured || !file || !key.trim() || request.current) return; + const controller = new AbortController(); + request.current = controller; + setPending(true); + setError(null); + setText(null); + try { + const result = await transcribeAudio(audio.transcriptionEndpoint, audio.transcriptionModel, key, file, controller.signal); + if (request.current === controller) setText(result); + } catch (failure) { + if (request.current === controller && !controller.signal.aborted) setError(failure instanceof AudioApiError ? failure.code : "network"); + } finally { + if (request.current === controller) { request.current = null; setPending(false); } + } + }; + return
+
+

{t("audio.dictation")}

+ {t(audio?.transcriptionConfigured ? "audio.configured" : audio ? "audio.notConfigured" : "audio.unknown")} +
+ {audio && <> +
{audio.transcriptionModel}
+
{ event.preventDefault(); void upload(); }}> + { cancel(); setError(null); setKey(value); }} /> + +
+ + {pending && } +
+ + + {text !== null &&

{t("audio.transcript")}

{text ? :

{t("audio.emptyTranscript")}

}
} +
{t("audio.examples")} + +

{t("audio.streaming")}

{t(audio.dictationConfigured ? "audio.configured" : "audio.notConfigured")}
+ + +
+ } +
; +} + +export function LiveVoicePanel({ audio }: { audio?: AudioApiInfo }) { + const t = useT(); + const [key, setKey] = useState(""); + const [state, setState] = useState("idle"); + const [error, setError] = useState(null); + const [events, setEvents] = useState([]); + const connection = useRef<{ dispose?: () => void } | null>(null); + const id = useId(); + useEffect(() => () => { connection.current?.dispose?.(); connection.current = null; }, []); + const disconnect = () => { + connection.current?.dispose?.(); connection.current = null; + setState("disconnected"); + }; + const connect = () => { + if (!audio?.liveConfigured || !key.trim() || connection.current) return; + setError(null); setEvents([]); + const current: { dispose?: () => void } = {}; + connection.current = current; + try { + current.dispose = connectLiveAudio({ endpoint: audio.liveEndpoint, model: audio.liveModel, key, + onState: (next, code) => { + if (connection.current !== current) return; + setState(next); setError(code ?? null); + if (next === "failed" || next === "disconnected") connection.current = null; + }, + onEvent: type => { if (connection.current === current) setEvents(previous => [...previous.slice(-7), type]); }, + }); + } catch (failure) { + connection.current = null; + setState("failed"); setError(failure instanceof AudioApiError ? failure.code : "network"); + } + }; + const busy = state === "connecting" || state === "connected"; + return
+

{t("audio.liveVoice")}

{t(audio?.liveConfigured ? "audio.configured" : audio ? "audio.notConfigured" : "audio.unknown")}
+ {audio && <> +
{audio.liveModel}
+
{ event.preventDefault(); connect(); }}> + { disconnect(); setError(null); setEvents([]); setKey(value); }} /> +
+ + {busy && } + {t(`audio.state.${state}`)} +
+ + + {events.length > 0 &&
{events.join("\n")}
} +
{t("audio.examples")}
+ } +
; +} diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index db6b8c889f..dea707e149 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -5,6 +5,35 @@ import type { TKey } from "./en"; * German i18n catalog, generated from en.ts. Must match the `TKey` set (compile-checked). */ export const de: Record = { + "audio.dictation": "Diktat", + "audio.liveVoice": "Live-Sprache", + "audio.configured": "Konfiguriert, nicht geprüft", + "audio.notConfigured": "Nicht konfiguriert", + "audio.unknown": "Audio-Metadaten nicht verfügbar", + "audio.key": "OpenCodex-Datenschlüssel", + "audio.file": "Audiodatei (max. 25 MB)", + "audio.transcribe": "Transkribieren", + "audio.transcribing": "Transkription läuft...", + "audio.transcript": "Transkript", + "audio.emptyTranscript": "Keine Sprache erkannt", + "audio.examples": "API-Beispiele", + "audio.streaming": "Streaming-Diktat", + "audio.connect": "Verbindung prüfen", + "audio.disconnect": "Trennen", + "audio.events": "Sitzungsereignisse", + "audio.state.idle": "Nicht geprüft", + "audio.state.connecting": "Verbindung wird aufgebaut...", + "audio.state.connected": "Sitzung bereit", + "audio.state.disconnected": "Getrennt", + "audio.state.failed": "Verbindung fehlgeschlagen", + "audio.error.auth": "Schlüssel abgelehnt. OpenCodex-Datenschlüssel prüfen.", + "audio.error.unavailable": "Anbieter nicht verfügbar. Konto prüfen.", + "audio.error.rateLimit": "Anfragelimit erreicht. Später erneut versuchen.", + "audio.error.invalid": "Anfrage abgelehnt. Datei und Anbieter prüfen.", + "audio.error.size": "Eine nicht leere Audiodatei bis 25 MB wählen.", + "audio.error.network": "Verbindung fehlgeschlagen. Proxy-Adresse prüfen.", + "audio.error.timeout": "Zeitüberschreitung. Erneut versuchen.", + "audio.error.protocol": "Unerwartete Audioantwort. Anbieterkompatibilität prüfen.", "models.pickerOrder.label": "Modellreihenfolge", "models.pickerOrder.default": "Standard", "models.pickerOrder.alphabetical": "A–Z nach Modell", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index 74e4ded4d1..50e709f16d 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -6,6 +6,35 @@ * `{var}` are plain interpolations. */ export const en = { + "audio.dictation": "Dictation", + "audio.liveVoice": "Live Voice", + "audio.configured": "Configured, not verified", + "audio.notConfigured": "Not configured", + "audio.unknown": "Audio metadata unavailable", + "audio.key": "OpenCodex data key", + "audio.file": "Audio file (25 MB max)", + "audio.transcribe": "Transcribe", + "audio.transcribing": "Transcribing...", + "audio.transcript": "Transcript", + "audio.emptyTranscript": "No speech detected", + "audio.examples": "API examples", + "audio.streaming": "Streaming dictation", + "audio.connect": "Check connection", + "audio.disconnect": "Disconnect", + "audio.events": "Session events", + "audio.state.idle": "Not checked", + "audio.state.connecting": "Connecting...", + "audio.state.connected": "Session ready", + "audio.state.disconnected": "Disconnected", + "audio.state.failed": "Connection failed", + "audio.error.auth": "Key rejected. Check your OpenCodex data key.", + "audio.error.unavailable": "Upstream unavailable. Check the provider account.", + "audio.error.rateLimit": "Rate limit reached. Try again later.", + "audio.error.invalid": "Request rejected. Check the file and provider.", + "audio.error.size": "Choose a nonempty audio file up to 25 MB.", + "audio.error.network": "Connection failed. Check the proxy address.", + "audio.error.timeout": "The request timed out. Try again.", + "audio.error.protocol": "Unexpected audio response. Check provider compatibility.", "models.pickerOrder.label": "Picker order", "models.pickerOrder.default": "Default", "models.pickerOrder.alphabetical": "A–Z by model", diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index 2bc3d5ab51..b448dc37e4 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -4,6 +4,35 @@ import type { TKey } from "./en"; * French i18n catalog. Must match the `TKey` set. */ export const fr: Record = { + "audio.dictation": "Dictée", + "audio.liveVoice": "Voix en direct", + "audio.configured": "Configuré, non vérifié", + "audio.notConfigured": "Non configuré", + "audio.unknown": "Métadonnées audio indisponibles", + "audio.key": "Clé de données OpenCodex", + "audio.file": "Fichier audio (25 Mo max.)", + "audio.transcribe": "Transcrire", + "audio.transcribing": "Transcription en cours...", + "audio.transcript": "Transcription", + "audio.emptyTranscript": "Aucune parole détectée", + "audio.examples": "Exemples d’API", + "audio.streaming": "Dictée en continu", + "audio.connect": "Vérifier la connexion", + "audio.disconnect": "Déconnecter", + "audio.events": "Événements de session", + "audio.state.idle": "Non vérifié", + "audio.state.connecting": "Connexion en cours...", + "audio.state.connected": "Session prête", + "audio.state.disconnected": "Déconnecté", + "audio.state.failed": "Échec de connexion", + "audio.error.auth": "Clé refusée. Vérifiez votre clé de données OpenCodex.", + "audio.error.unavailable": "Fournisseur indisponible. Vérifiez le compte.", + "audio.error.rateLimit": "Limite de requêtes atteinte. Réessayez plus tard.", + "audio.error.invalid": "Requête refusée. Vérifiez le fichier et le fournisseur.", + "audio.error.size": "Choisissez un fichier audio non vide de 25 Mo maximum.", + "audio.error.network": "Connexion impossible. Vérifiez l’adresse du proxy.", + "audio.error.timeout": "Délai dépassé. Réessayez.", + "audio.error.protocol": "Réponse audio inattendue. Vérifiez la compatibilité du fournisseur.", "models.pickerOrder.label": "Ordre des modèles", "models.pickerOrder.default": "Par défaut", "models.pickerOrder.alphabetical": "A–Z par modèle", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index a787ab735b..24bfae9d58 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -4,6 +4,35 @@ import type { TKey } from "./en"; * Japanese i18n catalog; must match the `TKey` set (compile-checked). */ export const ja: Record = { + "audio.dictation": "音声入力", + "audio.liveVoice": "リアルタイム音声", + "audio.configured": "設定済み・接続未確認", + "audio.notConfigured": "未設定", + "audio.unknown": "音声API情報を取得できません", + "audio.key": "OpenCodexデータキー", + "audio.file": "音声ファイル(最大25 MB)", + "audio.transcribe": "文字起こし", + "audio.transcribing": "変換中...", + "audio.transcript": "文字起こし結果", + "audio.emptyTranscript": "音声が検出されませんでした", + "audio.examples": "APIの例", + "audio.streaming": "ストリーミング音声入力", + "audio.connect": "接続を確認", + "audio.disconnect": "切断", + "audio.events": "セッションイベント", + "audio.state.idle": "未確認", + "audio.state.connecting": "接続中...", + "audio.state.connected": "セッション準備完了", + "audio.state.disconnected": "切断済み", + "audio.state.failed": "接続失敗", + "audio.error.auth": "キーが拒否されました。OpenCodexデータキーを確認してください。", + "audio.error.unavailable": "プロバイダーに接続できません。アカウントを確認してください。", + "audio.error.rateLimit": "リクエスト上限に達しました。後でもう一度お試しください。", + "audio.error.invalid": "リクエストが拒否されました。ファイルとプロバイダーを確認してください。", + "audio.error.size": "空でない25 MB以下の音声ファイルを選択してください。", + "audio.error.network": "接続に失敗しました。プロキシのアドレスを確認してください。", + "audio.error.timeout": "応答がタイムアウトしました。もう一度お試しください。", + "audio.error.protocol": "音声の応答形式が異なります。プロバイダーの互換性を確認してください。", "models.pickerOrder.label": "モデル選択順", "models.pickerOrder.default": "デフォルト", "models.pickerOrder.alphabetical": "モデル名のA–Z順", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index ccd2b06735..c712e64424 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -4,6 +4,35 @@ import type { TKey } from "./en"; * Korean i18n catalog; must match the `TKey` set (compile-checked). */ export const ko: Record = { + "audio.dictation": "받아쓰기", + "audio.liveVoice": "실시간 음성", + "audio.configured": "설정됨 · 연결 미확인", + "audio.notConfigured": "설정 안 됨", + "audio.unknown": "음성 API 정보를 불러올 수 없음", + "audio.key": "OpenCodex 데이터 키", + "audio.file": "음성 파일 (최대 25 MB)", + "audio.transcribe": "받아쓰기", + "audio.transcribing": "변환 중...", + "audio.transcript": "변환 결과", + "audio.emptyTranscript": "감지된 음성 없음", + "audio.examples": "API 예제", + "audio.streaming": "스트리밍 받아쓰기", + "audio.connect": "연결 확인", + "audio.disconnect": "연결 해제", + "audio.events": "세션 이벤트", + "audio.state.idle": "확인 전", + "audio.state.connecting": "연결 중...", + "audio.state.connected": "세션 준비됨", + "audio.state.disconnected": "연결 해제됨", + "audio.state.failed": "연결 실패", + "audio.error.auth": "키가 거부됐습니다. OpenCodex 데이터 키를 확인하세요.", + "audio.error.unavailable": "제공자에 연결할 수 없습니다. 계정을 확인하세요.", + "audio.error.rateLimit": "요청 한도에 도달했습니다. 잠시 후 다시 시도하세요.", + "audio.error.invalid": "요청이 거부됐습니다. 파일과 제공자를 확인하세요.", + "audio.error.size": "내용이 있는 25 MB 이하 음성 파일을 선택하세요.", + "audio.error.network": "연결에 실패했습니다. 프록시 주소를 확인하세요.", + "audio.error.timeout": "응답 시간이 초과됐습니다. 다시 시도하세요.", + "audio.error.protocol": "음성 응답 형식이 맞지 않습니다. 제공자 호환성을 확인하세요.", "models.pickerOrder.label": "모델 선택 순서", "models.pickerOrder.default": "기본값", "models.pickerOrder.alphabetical": "모델 이름순", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index ceb0c18a92..c4a3ae32c6 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -4,6 +4,35 @@ import type { TKey } from "./en"; * Russian i18n catalog; must match the `TKey` set (compile-checked). */ export const ru: Record = { + "audio.dictation": "Диктовка", + "audio.liveVoice": "Голос в реальном времени", + "audio.configured": "Настроено, не проверено", + "audio.notConfigured": "Не настроено", + "audio.unknown": "Метаданные аудио недоступны", + "audio.key": "Ключ данных OpenCodex", + "audio.file": "Аудиофайл (до 25 МБ)", + "audio.transcribe": "Расшифровать", + "audio.transcribing": "Расшифровка...", + "audio.transcript": "Расшифровка", + "audio.emptyTranscript": "Речь не обнаружена", + "audio.examples": "Примеры API", + "audio.streaming": "Потоковая диктовка", + "audio.connect": "Проверить соединение", + "audio.disconnect": "Отключить", + "audio.events": "События сеанса", + "audio.state.idle": "Не проверено", + "audio.state.connecting": "Подключение...", + "audio.state.connected": "Сеанс готов", + "audio.state.disconnected": "Отключено", + "audio.state.failed": "Ошибка подключения", + "audio.error.auth": "Ключ отклонён. Проверьте ключ данных OpenCodex.", + "audio.error.unavailable": "Провайдер недоступен. Проверьте аккаунт.", + "audio.error.rateLimit": "Достигнут лимит запросов. Повторите позже.", + "audio.error.invalid": "Запрос отклонён. Проверьте файл и провайдера.", + "audio.error.size": "Выберите непустой аудиофайл размером до 25 МБ.", + "audio.error.network": "Ошибка подключения. Проверьте адрес прокси.", + "audio.error.timeout": "Время ожидания истекло. Повторите попытку.", + "audio.error.protocol": "Неожиданный аудиоответ. Проверьте совместимость провайдера.", "models.pickerOrder.label": "Порядок моделей", "models.pickerOrder.default": "По умолчанию", "models.pickerOrder.alphabetical": "По имени A–Z", diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index 26a8f93d09..b17b909e99 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -5,6 +5,35 @@ import type { TKey } from "./en"; * Turkish i18n catalog. Must match the `TKey` set (compile-checked). */ export const tr: Record = { + "audio.dictation": "Dikte", + "audio.liveVoice": "Canlı Ses", + "audio.configured": "Yapılandırıldı, doğrulanmadı", + "audio.notConfigured": "Yapılandırılmadı", + "audio.unknown": "Ses API bilgileri kullanılamıyor", + "audio.key": "OpenCodex veri anahtarı", + "audio.file": "Ses dosyası (en fazla 25 MB)", + "audio.transcribe": "Metne çevir", + "audio.transcribing": "Metne çevriliyor...", + "audio.transcript": "Döküm", + "audio.emptyTranscript": "Konuşma algılanmadı", + "audio.examples": "API örnekleri", + "audio.streaming": "Akışlı dikte", + "audio.connect": "Bağlantıyı kontrol et", + "audio.disconnect": "Bağlantıyı kes", + "audio.events": "Oturum olayları", + "audio.state.idle": "Kontrol edilmedi", + "audio.state.connecting": "Bağlanıyor...", + "audio.state.connected": "Oturum hazır", + "audio.state.disconnected": "Bağlantı kesildi", + "audio.state.failed": "Bağlantı başarısız", + "audio.error.auth": "Anahtar reddedildi. OpenCodex veri anahtarını kontrol edin.", + "audio.error.unavailable": "Sağlayıcı kullanılamıyor. Hesabı kontrol edin.", + "audio.error.rateLimit": "İstek sınırına ulaşıldı. Daha sonra tekrar deneyin.", + "audio.error.invalid": "İstek reddedildi. Dosyayı ve sağlayıcıyı kontrol edin.", + "audio.error.size": "En fazla 25 MB boyutunda, boş olmayan bir ses dosyası seçin.", + "audio.error.network": "Bağlantı başarısız. Proxy adresini kontrol edin.", + "audio.error.timeout": "İstek zaman aşımına uğradı. Tekrar deneyin.", + "audio.error.protocol": "Beklenmeyen ses yanıtı. Sağlayıcı uyumluluğunu kontrol edin.", "models.pickerOrder.label": "Model sırası", "models.pickerOrder.default": "Varsayılan", "models.pickerOrder.alphabetical": "Model adına göre A–Z", diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index 1fb8387f4d..723f54b1e1 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -2,6 +2,35 @@ import type { TKey } from "./en"; /** Traditional Chinese (Taiwan) UI strings — keys must match `en.ts` 1:1. */ export const zhTW: Record = { + "audio.dictation": "聽寫", + "audio.liveVoice": "即時語音", + "audio.configured": "已設定,未驗證", + "audio.notConfigured": "未設定", + "audio.unknown": "無法取得音訊 API 資訊", + "audio.key": "OpenCodex 資料金鑰", + "audio.file": "音訊檔案(最大 25 MB)", + "audio.transcribe": "轉寫", + "audio.transcribing": "正在轉寫...", + "audio.transcript": "轉寫結果", + "audio.emptyTranscript": "未偵測到語音", + "audio.examples": "API 範例", + "audio.streaming": "串流聽寫", + "audio.connect": "檢查連線", + "audio.disconnect": "中斷連線", + "audio.events": "工作階段事件", + "audio.state.idle": "未檢查", + "audio.state.connecting": "正在連線...", + "audio.state.connected": "工作階段已就緒", + "audio.state.disconnected": "已中斷", + "audio.state.failed": "連線失敗", + "audio.error.auth": "金鑰遭拒。請檢查 OpenCodex 資料金鑰。", + "audio.error.unavailable": "上游無法使用。請檢查供應商帳戶。", + "audio.error.rateLimit": "已達請求上限。請稍後再試。", + "audio.error.invalid": "請求遭拒。請檢查檔案與供應商。", + "audio.error.size": "請選擇不超過 25 MB 的非空音訊檔案。", + "audio.error.network": "連線失敗。請檢查代理位址。", + "audio.error.timeout": "請求逾時。請再試一次。", + "audio.error.protocol": "音訊回應格式異常。請檢查供應商相容性。", "models.pickerOrder.label": "模型選擇順序", "models.pickerOrder.default": "預設", "models.pickerOrder.alphabetical": "依模型名稱 A–Z", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index 4e2cd831cc..c36380c0d7 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -4,6 +4,35 @@ import type { TKey } from "./en"; * Chinese i18n catalog; must match the `TKey` set (compile-checked). */ export const zh: Record = { + "audio.dictation": "听写", + "audio.liveVoice": "实时语音", + "audio.configured": "已配置,未验证", + "audio.notConfigured": "未配置", + "audio.unknown": "无法获取音频 API 信息", + "audio.key": "OpenCodex 数据密钥", + "audio.file": "音频文件(最大 25 MB)", + "audio.transcribe": "转写", + "audio.transcribing": "正在转写...", + "audio.transcript": "转写结果", + "audio.emptyTranscript": "未检测到语音", + "audio.examples": "API 示例", + "audio.streaming": "流式听写", + "audio.connect": "检查连接", + "audio.disconnect": "断开连接", + "audio.events": "会话事件", + "audio.state.idle": "未检查", + "audio.state.connecting": "正在连接...", + "audio.state.connected": "会话已就绪", + "audio.state.disconnected": "已断开", + "audio.state.failed": "连接失败", + "audio.error.auth": "密钥被拒绝。请检查 OpenCodex 数据密钥。", + "audio.error.unavailable": "上游不可用。请检查提供商账户。", + "audio.error.rateLimit": "已达到请求上限。请稍后重试。", + "audio.error.invalid": "请求被拒绝。请检查文件和提供商。", + "audio.error.size": "请选择不超过 25 MB 的非空音频文件。", + "audio.error.network": "连接失败。请检查代理地址。", + "audio.error.timeout": "请求超时。请重试。", + "audio.error.protocol": "音频响应格式异常。请检查提供商兼容性。", "models.pickerOrder.label": "模型选择顺序", "models.pickerOrder.default": "默认", "models.pickerOrder.alphabetical": "按模型名 A–Z", diff --git a/gui/src/pages/ApiKeys.tsx b/gui/src/pages/ApiKeys.tsx index d10ff3c33d..ca45d8b940 100644 --- a/gui/src/pages/ApiKeys.tsx +++ b/gui/src/pages/ApiKeys.tsx @@ -20,6 +20,7 @@ import { deriveApiEndpoints, isApiAuthMatrix, isApiKeyUsage, + isAudioApiInfo, type ApiEndpointInfo, type ApiAuthMatrixRow, type ApiKeyEntry, @@ -43,6 +44,7 @@ interface KeysResponse { messagesEndpoint?: string; modelsEndpoint?: string; claudeCodeEnabled?: boolean; + audio?: unknown; } interface CreateKeyResponse { @@ -88,6 +90,10 @@ function seedEndpointsFromApiBase(apiBase: string): ApiEndpointInfo { function validCachedKeys(cached: CachedKeysShape | null): CachedKeysShape | null { if (!cached || !isApiAuthMatrix(cached.authMatrix)) return null; if (!Array.isArray(cached.keys) || cached.keys.some(key => !key || !isApiKeyUsage(key.usage) || !validPendingRotation(key.pendingRotation))) return null; + if (cached.endpoints?.audio !== undefined && !isAudioApiInfo(cached.endpoints.audio, cached.endpoints.baseUrl)) { + const { audio: _audio, ...endpoints } = cached.endpoints; + return { ...cached, endpoints }; + } return cached; } @@ -155,6 +161,7 @@ export default function ApiKeys({ apiBase, active = true }: { apiBase: string; a chatCompletions: data.chatCompletionsEndpoint ?? derived.chatCompletions, messages: data.messagesEndpoint ?? derived.messages, models: data.modelsEndpoint ?? derived.models, + ...(isAudioApiInfo(data.audio, data.baseUrl ?? derived.baseUrl) ? { audio: data.audio } : {}), }, claudeCodeEnabled: data.claudeCodeEnabled !== false, ...(data.attributionSince ? { attributionSince: data.attributionSince } : {}), @@ -496,6 +503,7 @@ export default function ApiKeys({ apiBase, active = true }: { apiBase: string; a ) : ( <> ; + try { + const base = new URL(baseUrl); + if (!["http:", "https:"].includes(base.protocol) || base.username || base.password || base.search || base.hash) return false; + const paths = { + transcriptionEndpoint: ["/audio/transcriptions", false], + dictationStreamEndpoint: ["/audio/transcriptions/stream", true], + liveEndpoint: ["/live", true], + realtimeCallsEndpoint: ["/realtime/calls", false], + } as const; + for (const [field, [suffix, socket]] of Object.entries(paths)) { + if (typeof audio[field] !== "string") return false; + const url = new URL(audio[field]); + const protocol = socket ? (base.protocol === "https:" ? "wss:" : "ws:") : base.protocol; + if (url.protocol !== protocol || url.username || url.password || url.search || url.hash) return false; + url.protocol = base.protocol; + if (url.origin !== base.origin || url.pathname !== `${base.pathname.replace(/\/$/, "")}${suffix}`) return false; + } + return audio.transcriptionModel === "gpt-4o-transcribe" && audio.liveModel === "gpt-live-1-codex" + && [audio.transcriptionConfigured, audio.dictationConfigured, audio.liveConfigured].every(flag => typeof flag === "boolean"); + } catch { return false; } } export type ModelTestState = "idle" | "testing" | "ok" | "error"; diff --git a/gui/src/styles-apikeys-workspace.css b/gui/src/styles-apikeys-workspace.css index ad027753eb..dbe47c1d94 100644 --- a/gui/src/styles-apikeys-workspace.css +++ b/gui/src/styles-apikeys-workspace.css @@ -69,6 +69,63 @@ min-width: 0; } +.audio-api-section { + min-width: 0; + display: grid; + gap: var(--space-3); + padding: 18px 0; + border-top: 1px solid var(--border); + letter-spacing: 0; +} + +.audio-api-head, .audio-api-actions { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: var(--space-2); + min-width: 0; +} + +.audio-api-head { justify-content: space-between; } +.audio-api-head h3, .audio-api-section h4 { + margin: 0; + font-size: var(--text-control); + line-height: var(--leading-ui); + text-wrap: balance; + overflow-wrap: anywhere; +} +.audio-api-endpoints, .audio-api-field, .audio-api-form, .audio-api-result { + display: grid; + gap: var(--space-2); + min-width: 0; +} +.audio-api-form { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } +.audio-api-field { font-size: var(--text-label); align-content: start; } +.audio-api-field .input { width: 100%; min-width: 0; box-sizing: border-box; } +.audio-api-field input[type="file"] { padding: 7px; } +.audio-api-actions { grid-column: 1 / -1; min-height: 36px; } +.audio-api-actions .btn { min-height: 36px; white-space: nowrap; } +.audio-api-status { font-size: var(--text-label); } +.audio-api-error { margin: 0; color: var(--danger); font-size: var(--text-label); } +.audio-api-endpoints .api-endpoint-url { white-space: normal; overflow-wrap: anywhere; } +.audio-api-examples { min-width: 0; } +.audio-api-examples summary { cursor: pointer; font-size: var(--text-label); padding: 6px 0; } +.audio-api-examples > :not(summary) { margin-top: var(--space-2); } +.audio-api-events { + margin: 0; + padding: var(--space-2) 0; + white-space: pre-wrap; + overflow-wrap: anywhere; + font-size: var(--text-label); + color: var(--muted); +} +.audio-api-result .api-example-pre { white-space: pre-wrap; overflow-wrap: anywhere; } +@container apikeys-workspace (max-width: 560px) { + .audio-api-form { grid-template-columns: minmax(0, 1fr); } + .audio-api-actions .btn { min-height: 44px; } + .audio-api-field .input { min-height: 44px; } +} + .awi-keylist-panel .awi-keylist-name { appearance: none; background: none; diff --git a/gui/tests/api-auth-memory.test.ts b/gui/tests/api-auth-memory.test.ts index 825df3806d..96c618f6bd 100644 --- a/gui/tests/api-auth-memory.test.ts +++ b/gui/tests/api-auth-memory.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, expect, test } from "bun:test"; import { Window } from "happy-dom"; -import { configureApiTargets, installApiAuthFetch, installApiSessionFromHtml, resetApiAuthFetchForTests } from "../src/api"; +import { configureApiTargets, fetchAudioUpload, installApiAuthFetch, installApiSessionFromHtml, resetApiAuthFetchForTests } from "../src/api"; import { targetsFromMachineStatus, type MachineStatusV1 } from "../src/api-targets"; const LEGACY_TOKEN_KEY = "opencodex-api-token"; @@ -86,6 +86,29 @@ test("installApiAuthFetch deletes legacy sessionStorage token without reading it } }); +test("audio uploads bypass connected management interception and 401 recovery", async () => { + injectSessionMeta("ocx_session_audio_machine", "audio-csrf", "http://localhost"); + const seen: Array<{ url: string; headers: Headers }> = []; + const mockFetch = (async (input: RequestInfo | URL, init?: RequestInit) => { + seen.push({ url: String(input), headers: new Headers(init?.headers) }); + return new Response("rejected", { status: 401 }); + }) as typeof fetch; + await installMockAuthFetch(mockFetch); + configureApiTargets({ + connected: true, + machine: { id: "machine", baseUrl: "http://localhost", serverOrigin: "http://localhost", bootstrapPath: "/opencodex-session", transport: "same-origin" }, + shared: { id: "shared", baseUrl: "https://hub.example.test", serverOrigin: "https://hub.example.test", bootstrapPath: "https://hub.example.test/opencodex-session", transport: "relay" }, + }); + const key = "ocx_data_audio_wrapper_fixture"; + const response = await fetchAudioUpload("https://hub.example.test/v1/audio/transcriptions", { method: "POST", headers: { "X-OpenCodex-API-Key": key }, body: new FormData() }); + expect(response.status).toBe(401); + expect(seen).toHaveLength(1); + expect([...seen[0]!.headers]).toEqual([["x-opencodex-api-key", key]]); + expect(sessionStorage.length).toBe(0); + await expect(fetchAudioUpload("https://hub.example.test/api/config", { method: "POST" })).rejects.toThrow(); + expect(seen).toHaveLength(1); +}); + test("prompted API tokens stay memory-only and are not written to sessionStorage", async () => { declareManagementAuthRequired(); sessionStorage.setItem(LEGACY_TOKEN_KEY, "legacy-secret"); diff --git a/gui/tests/audio-api-client.test.ts b/gui/tests/audio-api-client.test.ts new file mode 100644 index 0000000000..1d0aa25fbc --- /dev/null +++ b/gui/tests/audio-api-client.test.ts @@ -0,0 +1,145 @@ +import { afterEach, expect, test } from "bun:test"; +import { AUDIO_FILE_MAX_BYTES, AudioApiError, LIVE_SESSION_UPDATE, audioSocketProtocols, connectLiveAudio, transcribeAudio } from "../src/audio-api-client"; +import { resetApiAuthFetchForTests } from "../src/api"; +import { isAudioApiInfo, type AudioApiInfo } from "../src/pages/api-keys-utils"; + +const originalFetch = globalThis.fetch; +const originalSocket = globalThis.WebSocket; +const KEY = "ocx_data_audio_client_fixture"; +const ENDPOINT = "https://gateway.example/v1/audio/transcriptions"; +afterEach(() => { globalThis.fetch = originalFetch; globalThis.WebSocket = originalSocket; resetApiAuthFetchForTests(); }); + +test("upload sends multipart with only the typed data key and no cookies or redirects", async () => { + resetApiAuthFetchForTests(); + let init!: RequestInit; + globalThis.fetch = (async (_url, options) => { init = options!; return Response.json({ text: "fixture transcript" }); }) as typeof fetch; + const text = await transcribeAudio(ENDPOINT, "gpt-4o-transcribe", KEY, new File(["synthetic"], "fixture.wav"), new AbortController().signal); + expect(text).toBe("fixture transcript"); + expect([...new Headers(init.headers)]).toEqual([["x-opencodex-api-key", KEY]]); + expect(init.credentials).toBe("omit"); expect(init.redirect).toBe("error"); + const body = init.body as FormData; + expect(body.get("model")).toBe("gpt-4o-transcribe"); + expect(body.get("response_format")).toBe("json"); + expect((body.get("file") as File).name).toBe("fixture.wav"); +}); + +test("invalid files stop before fetch and upstream errors never expose their body", async () => { + resetApiAuthFetchForTests(); + let calls = 0; + globalThis.fetch = (async () => { calls++; return new Response("private upstream detail", { status: 401 }); }) as typeof fetch; + const file = new File(["fixture"], "fixture.wav"); + for (const size of [0, AUDIO_FILE_MAX_BYTES + 1]) { + Object.defineProperty(file, "size", { configurable: true, value: size }); + await expect(transcribeAudio(ENDPOINT, "gpt-4o-transcribe", KEY, file, new AbortController().signal)).rejects.toMatchObject({ code: "size" }); + } + expect(calls).toBe(0); + Object.defineProperty(file, "size", { configurable: true, value: 7 }); + await expect(transcribeAudio(ENDPOINT, "gpt-4o-transcribe", KEY, file, new AbortController().signal)).rejects.toEqual(new AudioApiError("auth")); +}); + +test("upload cancellation aborts fetch and a locked response body", async () => { + resetApiAuthFetchForTests(); + let responseCancelled = false; + let uploadSignal: AbortSignal | undefined; + let bodyStarted!: () => void; + const started = new Promise(resolve => { bodyStarted = resolve; }); + globalThis.fetch = (async (_url, init) => { + uploadSignal = init!.signal!; + return new Response(new ReadableStream({ start() { bodyStarted(); }, cancel() { responseCancelled = true; } })); + }) as typeof fetch; + const controller = new AbortController(); + const pending = transcribeAudio(ENDPOINT, "gpt-4o-transcribe", KEY, new File(["x"], "x.wav"), controller.signal); + await started; + await Promise.resolve(); + controller.abort(); + await expect(pending).rejects.toBeDefined(); + expect(uploadSignal!.aborted).toBe(true); + expect(responseCancelled).toBe(true); +}); + +test("audio metadata accepts the exact scheme/origin/path projection only", () => { + const base = "https://[2001:db8::1]:8443/v1"; + const audio: AudioApiInfo = { + transcriptionEndpoint: `${base}/audio/transcriptions`, realtimeCallsEndpoint: `${base}/realtime/calls`, + liveEndpoint: "wss://[2001:db8::1]:8443/v1/live", dictationStreamEndpoint: "wss://[2001:db8::1]:8443/v1/audio/transcriptions/stream", + transcriptionModel: "gpt-4o-transcribe", liveModel: "gpt-live-1-codex", + transcriptionConfigured: true, dictationConfigured: true, liveConfigured: true, + }; + expect(isAudioApiInfo(audio, base)).toBe(true); + for (const endpoint of ["ws://[2001:db8::1]:8443/v1/live", `${audio.liveEndpoint}?key=value`, `${audio.liveEndpoint}#fragment`, "wss://user:pass@[2001:db8::1]:8443/v1/live", "wss://other.example/v1/live", "wss://[2001:db8::1]:8443/v1/responses"]) { + expect(isAudioApiInfo({ ...audio, liveEndpoint: endpoint }, base)).toBe(false); + } + expect(isAudioApiInfo(undefined, base)).toBe(false); + expect(isAudioApiInfo({ ...audio, liveConfigured: "true" }, base)).toBe(false); +}); + +class FakeSocket { + static OPEN = 1; + static latest: FakeSocket; + readyState = 0; + closed = false; + sent: string[] = []; + onopen: (() => void) | null = null; + onmessage: ((event: { data: string }) => void) | null = null; + onclose: ((event: { code: number }) => void) | null = null; + onerror: (() => void) | null = null; + constructor(readonly url: string, readonly protocols: string[]) { FakeSocket.latest = this; } + send(value: string) { this.sent.push(value); } + close() { this.closed = true; this.readyState = 3; } + open() { this.readyState = 1; this.onopen?.(); } + message(value: unknown) { this.onmessage?.({ data: JSON.stringify(value) }); } +} + +function probe(options: { readyTimeoutMs?: number; maxSessionMs?: number } = {}) { + globalThis.WebSocket = FakeSocket as unknown as typeof WebSocket; + const states: string[] = []; + const events: string[] = []; + const dispose = connectLiveAudio({ endpoint: "wss://gateway.example/v1/live", model: "gpt-live-1-codex", key: KEY, + onState: (state, code) => states.push(code ? `${state}:${code}` : state), onEvent: event => events.push(event), ...options }); + return { socket: FakeSocket.latest, states, events, dispose }; +} + +test("voice waits for a nonterminal native session ID, then closes without audio", () => { + const { socket, states, events, dispose } = probe(); + try { + expect(socket.url).not.toContain(KEY); + expect(socket.protocols).toEqual(audioSocketProtocols(KEY)); + socket.open(); + expect(JSON.parse(socket.sent[0]!)).toEqual(LIVE_SESSION_UPDATE); + expect(states).toEqual(["connecting"]); + socket.message({ type: "session.started", session: { session_id: "dictation-not-live" } }); + expect(states).toEqual(["connecting"]); + socket.message({ type: "session.started", session: { id: "fixture" }, secret: "never display" }); + expect(states).toEqual(["connecting", "connected"]); + expect(events).toEqual(["session.started", "session.started"]); + } finally { dispose(); } + expect(socket.closed).toBe(true); + expect(socket.onmessage).toBeNull(); + expect(socket.sent.map(value => JSON.parse(value).type)).toEqual(["session.update", "session.close"]); +}); + +test("terminal acknowledgments and protocol errors stay failed after a normal close", () => { + for (const event of [{ type: "session.updated", session: { id: "fixture", status: "closed" } }, { type: "protocol.error", error: "sensitive" }]) { + const { socket, states, dispose } = probe(); + socket.open(); + const oldClose = socket.onclose; + socket.message(event); + oldClose?.({ code: 1000 }); + expect(states).toEqual(["connecting", "failed:protocol"]); + expect(socket.closed).toBe(true); + dispose(); + } +}); + +test("voice readiness and established-session timers release all handlers", async () => { + const pending = probe({ readyTimeoutMs: 5 }); + pending.socket.open(); + await new Promise(resolve => setTimeout(resolve, 20)); + expect(pending.states).toEqual(["connecting", "failed:timeout"]); + expect(pending.socket.onmessage).toBeNull(); + const ready = probe({ maxSessionMs: 5 }); + ready.socket.open(); ready.socket.message({ type: "session.started", session: { id: "fixture" } }); + await new Promise(resolve => setTimeout(resolve, 20)); + expect(ready.states).toEqual(["connecting", "connected", "disconnected"]); + expect(ready.socket.closed).toBe(true); +}); diff --git a/gui/tests/audio-api-panel.test.tsx b/gui/tests/audio-api-panel.test.tsx new file mode 100644 index 0000000000..fda94960db --- /dev/null +++ b/gui/tests/audio-api-panel.test.tsx @@ -0,0 +1,161 @@ +/** @jsxImportSource react */ +import { afterEach, beforeEach, expect, test } from "bun:test"; +import { Window } from "happy-dom"; +import { act } from "react"; +import type { Root } from "react-dom/client"; +import { LanguageProvider } from "../src/i18n/provider"; +import { clearClientResourceStoresForTests } from "../src/client-resource"; +import { resetApiAuthFetchForTests } from "../src/api"; +import ApiKeys from "../src/pages/ApiKeys"; + +const KEY = "ocx_data_audio_panel_fixture"; +const BASE = "http://localhost/v1"; +const AUDIO = { + transcriptionEndpoint: `${BASE}/audio/transcriptions`, dictationStreamEndpoint: "ws://localhost/v1/audio/transcriptions/stream", + liveEndpoint: "ws://localhost/v1/live", realtimeCallsEndpoint: `${BASE}/realtime/calls`, + transcriptionModel: "gpt-4o-transcribe", liveModel: "gpt-live-1-codex", + transcriptionConfigured: true, dictationConfigured: true, liveConfigured: true, +}; +const globals = ["document", "window", "navigator", "localStorage", "sessionStorage", "fetch", "WebSocket", "HTMLElement", "IS_REACT_ACT_ENVIRONMENT"] as const; +let previous: Record; +let win: Window; +let root: Root | undefined; +let container: HTMLDivElement; +let audio: unknown; +let sent: RequestInit[]; +let responder: (init: RequestInit) => Promise; + +class Socket { + static OPEN = 1; + static latest: Socket | undefined; + readyState = 0; + closed = false; + onopen: (() => void) | null = null; + onmessage: ((event: { data: string }) => void) | null = null; + onerror: (() => void) | null = null; + onclose: ((event: { code: number }) => void) | null = null; + sent: string[] = []; + constructor(readonly url: string, readonly protocols: string[]) { Socket.latest = this; } + send(value: string) { this.sent.push(value); } + close() { this.closed = true; } +} + +beforeEach(() => { + previous = Object.fromEntries(globals.map(key => [key, Object.getOwnPropertyDescriptor(globalThis, key)])); + win = new Window({ url: "http://localhost/" }); + for (const key of ["document", "window", "navigator", "localStorage", "sessionStorage", "HTMLElement"] as const) { + Object.defineProperty(globalThis, key, { configurable: true, value: key === "window" ? win : win[key] }); + } + Object.defineProperty(globalThis, "IS_REACT_ACT_ENVIRONMENT", { configurable: true, value: true }); + Object.defineProperty(globalThis, "WebSocket", { configurable: true, writable: true, value: Socket }); + win.localStorage.setItem("ocx-lang", "en"); + audio = AUDIO; sent = []; Socket.latest = undefined; + responder = async () => Response.json({ text: "Synthetic transcript" }); + Object.defineProperty(globalThis, "fetch", { configurable: true, writable: true, value: async (input: RequestInfo | URL, init?: RequestInit) => { + const url = String(input); + if (url.endsWith("/api/keys")) return Response.json({ keys: [], baseUrl: BASE, endpoint: `${BASE}/responses`, authMatrix: [{ endpoint: "/v1/models", bearer: "accepted", dedicated: "accepted", xApiKey: "accepted" }], ...(audio === undefined ? {} : { audio }) }); + if (url.endsWith("/v1/models")) return Response.json({ data: [] }); + if (url.endsWith("/v1/audio/transcriptions")) { sent.push(init!); return responder(init!); } + return new Response(null, { status: 404 }); + } }); + resetApiAuthFetchForTests(); clearClientResourceStoresForTests(); +}); + +afterEach(async () => { + if (root) await act(async () => { root!.unmount(); }); + root = undefined; + clearClientResourceStoresForTests(); resetApiAuthFetchForTests(); + win.close(); + for (const key of globals) { + if (previous[key]) Object.defineProperty(globalThis, key, previous[key]!); + else delete (globalThis as Record)[key]; + } +}); + +async function flush() { await act(async () => { await new Promise(resolve => setTimeout(resolve, 0)); }); } +async function render(active = true, apiBase = "http://localhost") { + if (!root) { + container = win.document.createElement("div") as unknown as HTMLDivElement; + win.document.body.appendChild(container); + root = (await import("react-dom/client")).createRoot(container); + } + await act(async () => { root!.render(); }); + await flush(); +} +async function typeKey(section: string) { + const input = container.querySelector(`${section} input[type=password]`)!; + await act(async () => { + Object.getOwnPropertyDescriptor(win.HTMLInputElement.prototype, "value")!.set!.call(input, KEY); + input.dispatchEvent(new win.Event("input", { bubbles: true }) as unknown as Event); + }); +} +async function selectFile() { + const input = container.querySelector('input[type="file"]')!; + Object.defineProperty(input, "files", { configurable: true, value: [new File(["synthetic"], "fixture.wav")] }); + await act(async () => { input.dispatchEvent(new win.Event("change", { bubbles: true }) as unknown as Event); }); +} +async function submit(section: string) { + await act(async () => { container.querySelector(`${section} button[type=submit]`)!.click(); }); + await flush(); +} +const DICTATION = "#api-section-dictation"; +const LIVE = "#api-section-live-voice"; + +test("real API page uploads, copies transcript, and keeps keys out of caches and examples", async () => { + await render(); + expect(sent).toHaveLength(0); + await typeKey(DICTATION); await selectFile(); await submit(DICTATION); + expect(sent).toHaveLength(1); + expect(new Headers(sent[0]!.headers).get("x-opencodex-api-key")).toBe(KEY); + expect(container.querySelector(".audio-api-result")?.textContent).toContain("Synthetic transcript"); + let copied = ""; + Object.defineProperty(navigator, "clipboard", { configurable: true, value: { writeText: async (text: string) => { copied = text; } } }); + await act(async () => { container.querySelector(".audio-api-result button")!.click(); }); + expect(copied).toBe("Synthetic transcript"); + expect(container.querySelector(".audio-api-examples")?.textContent).not.toContain(KEY); + for (const storage of [win.localStorage, win.sessionStorage]) { + for (let i = 0; i < storage.length; i++) expect(storage.getItem(storage.key(i)!)).not.toContain(KEY); + } +}); + +test("upload errors are localized, cancellation and inactive panels cannot publish late text", async () => { + await render(); await typeKey(DICTATION); await selectFile(); + responder = async () => new Response("private account details", { status: 401 }); + await submit(DICTATION); + expect(container.querySelector(`${DICTATION} [role=alert]`)?.textContent).toContain("Key rejected"); + expect(container.textContent).not.toContain("private account details"); + let resolveResponse!: (response: Response) => void; + responder = () => new Promise(resolve => { resolveResponse = resolve; }); + await submit(DICTATION); + const signal = sent.at(-1)!.signal!; + await render(false); + expect(signal.aborted).toBe(true); + await act(async () => { resolveResponse(Response.json({ text: "late transcript" })); }); + await render(true); + expect(container.textContent).not.toContain("late transcript"); + expect(container.querySelector(`${DICTATION} input[type=password]`)!.value).toBe(""); +}); + +test("voice reports real readiness, filters event payloads and releases on deactivation", async () => { + await render(); + expect(Socket.latest).toBeUndefined(); + await typeKey(LIVE); await submit(LIVE); + const socket = Socket.latest!; + expect(socket).toBeDefined(); + await act(async () => { socket.readyState = 1; socket.onopen?.(); }); + expect(container.querySelector(`${LIVE} [role=status]`)?.textContent).toBe("Connecting..."); + await act(async () => { socket.onmessage?.({ data: JSON.stringify({ type: "session.started", session: { id: "fixture" }, token: "never-render" }) }); }); + expect(container.querySelector(`${LIVE} [role=status]`)?.textContent).toBe("Session ready"); + expect(container.textContent).not.toContain("never-render"); + await render(false); + expect(socket.closed).toBe(true); expect(socket.onmessage).toBeNull(); +}); + +test("missing or malformed audio metadata leaves existing key management usable", async () => { + audio = { ...AUDIO, liveEndpoint: "wss://unexpected.example/v1/live" }; + await render(); + expect(container.querySelector(DICTATION)?.textContent).toContain("Audio metadata unavailable"); + expect(container.querySelector(`${DICTATION} input`)).toBeNull(); + expect(container.textContent).toContain("Generate"); + expect(sent).toHaveLength(0); +}); diff --git a/src/server/management/api-access.ts b/src/server/management/api-access.ts index 44336919ca..0421824e5a 100644 --- a/src/server/management/api-access.ts +++ b/src/server/management/api-access.ts @@ -2,6 +2,21 @@ import type { OcxConfig } from "../../types"; import { isWildcardHostname } from "../../codex/loopback-target"; import { localInferenceDestination } from "../../lib/local-destinations"; import { probeHostname } from "../proxy-liveness"; +import { isCanonicalOpenAiForwardProvider, OPENAI_API_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers-destination"; +import { LIVE_AUDIO_MODEL, TRANSCRIPTION_MODEL } from "../audio-upstream"; + +export interface AudioApiAccess { + transcriptionEndpoint: string; + dictationStreamEndpoint: string; + liveEndpoint: string; + realtimeCallsEndpoint: string; + transcriptionModel: string; + liveModel: string; + /** Configuration only, not account health, entitlement or observed connectivity. */ + transcriptionConfigured: boolean; + dictationConfigured: boolean; + liveConfigured: boolean; +} export interface ApiAccessEndpoints { baseUrl: string; @@ -10,6 +25,7 @@ export interface ApiAccessEndpoints { messagesEndpoint: string; modelsEndpoint: string; claudeCodeEnabled: boolean; + audio: AudioApiAccess; /** Back-compat alias for older GUI clients. */ endpoint: string; } @@ -140,6 +156,16 @@ export function buildApiAccessEndpoints( ): ApiAccessEndpoints { const baseUrl = resolveApiAccessBaseUrl(config, opts); const responsesEndpoint = `${baseUrl}/responses`; + const socketBase = new URL(baseUrl); + socketBase.protocol = socketBase.protocol === "https:" ? "wss:" : "ws:"; + const forward = config.providers?.[OPENAI_CODEX_PROVIDER_ID]; + const chatgptConfigured = !!forward && forward.disabled !== true + && isCanonicalOpenAiForwardProvider({ ...forward, authMode: forward.authMode ?? "forward" }); + const keyed = config.providers?.[OPENAI_API_PROVIDER_ID]; + // Do not resolve key references or inspect accounts on a management metadata read. + const apiConfigured = !!keyed && keyed.disabled !== true && keyed.adapter === "openai-responses" + && keyed.authMode !== "forward" && keyed.baseUrl.replace(/\/+$/, "") === "https://api.openai.com/v1" + && typeof keyed.apiKey === "string" && !!keyed.apiKey.trim(); return { baseUrl, responsesEndpoint, @@ -147,6 +173,17 @@ export function buildApiAccessEndpoints( messagesEndpoint: `${baseUrl}/messages`, modelsEndpoint: `${baseUrl}/models`, claudeCodeEnabled: config.claudeCode?.enabled !== false, + audio: { + transcriptionEndpoint: `${baseUrl}/audio/transcriptions`, + dictationStreamEndpoint: `${socketBase.href}/audio/transcriptions/stream`, + liveEndpoint: `${socketBase.href}/live`, + realtimeCallsEndpoint: `${baseUrl}/realtime/calls`, + transcriptionModel: TRANSCRIPTION_MODEL, + liveModel: LIVE_AUDIO_MODEL, + transcriptionConfigured: chatgptConfigured || apiConfigured, + dictationConfigured: chatgptConfigured, + liveConfigured: chatgptConfigured, + }, endpoint: responsesEndpoint, }; } diff --git a/structure/gui-and-management-api.md b/structure/gui-and-management-api.md index 73090d646e..78c3f6897a 100644 --- a/structure/gui-and-management-api.md +++ b/structure/gui-and-management-api.md @@ -290,6 +290,26 @@ hard-lock evidence and reset-notification history; their retention rules are doc ## Dashboard surfaces +`src/server/management/api-access.ts` publishes an `audio` projection through the +existing `/api/keys` response in `src/server/management/oauth-account-routes.ts`. +URLs derive from the same advertised inference base as text APIs, with HTTP(S) +mapped to WS(S). Configuration flags inspect enabled canonical providers only; +they do not read credentials, inspect account health or prove entitlement. +`gui/src/pages/api-keys-utils.ts` validates the same projection on network and +cache reads. Missing or malformed audio metadata disables only audio controls. + +Connections/API keys has separate Dictation and Live Voice sections in +`gui/src/components/apikeys-workspace/AudioApiPanel.tsx`. Transient data keys never +enter caches or generated samples. `gui/src/audio-api-client.ts` owns bounded +uploads and a connection-only native voice probe; `gui/src/api.ts` sends uploads +without management auth injection or 401 recovery. Voice readiness requires a +nonterminal session acknowledgment with `session.id`, not merely socket open. +Changing keys, inference metadata, API origin or leaving the active panel releases +requests/sockets. Only allowlisted event types and localized error categories are +displayed. Tests live in `gui/tests/audio-api-client.test.ts`, +`gui/tests/audio-api-panel.test.tsx`, `gui/tests/api-auth-memory.test.ts` and +`tests/server/api-access-endpoints.test.ts`. + Provider Overview consumes the existing shared `add-provider-presets` resource for sponsor presentation. `matchingWorkspacePreset` requires the configured id, adapter and normalized endpoint to match; a custom endpoint or absent sponsor metadata suppresses the introduction. diff --git a/tests/server/api-access-endpoints.test.ts b/tests/server/api-access-endpoints.test.ts index f1c6f7fa6d..ce2a55fdbf 100644 --- a/tests/server/api-access-endpoints.test.ts +++ b/tests/server/api-access-endpoints.test.ts @@ -7,7 +7,7 @@ import { describe("buildApiAccessEndpoints", () => { test("builds the external gateway URLs from hostname and port", () => { - expect(buildApiAccessEndpoints({ hostname: "127.0.0.1", port: 10100 })).toEqual({ + expect(buildApiAccessEndpoints({ hostname: "127.0.0.1", port: 10100 })).toMatchObject({ baseUrl: "http://127.0.0.1:10100/v1", endpoint: "http://127.0.0.1:10100/v1/responses", responsesEndpoint: "http://127.0.0.1:10100/v1/responses", @@ -19,7 +19,7 @@ describe("buildApiAccessEndpoints", () => { }); test("falls back to the default bind when config fields are missing", () => { - expect(buildApiAccessEndpoints({})).toEqual({ + expect(buildApiAccessEndpoints({})).toMatchObject({ baseUrl: "http://127.0.0.1:10100/v1", endpoint: "http://127.0.0.1:10100/v1/responses", responsesEndpoint: "http://127.0.0.1:10100/v1/responses", @@ -31,7 +31,7 @@ describe("buildApiAccessEndpoints", () => { }); test("brackets IPv6 hostnames for URL display", () => { - expect(buildApiAccessEndpoints({ hostname: "::1", port: 10100 })).toEqual({ + expect(buildApiAccessEndpoints({ hostname: "::1", port: 10100 })).toMatchObject({ baseUrl: "http://[::1]:10100/v1", endpoint: "http://[::1]:10100/v1/responses", responsesEndpoint: "http://[::1]:10100/v1/responses", @@ -106,6 +106,36 @@ describe("buildApiAccessEndpoints", () => { test("reflects disabled Claude inbound in API access metadata", () => { expect(buildApiAccessEndpoints({ claudeCode: { enabled: false } }).claudeCodeEnabled).toBe(false); }); + + test("audio metadata derives TLS and IPv6 URLs without claiming connectivity", () => { + const result = buildApiAccessEndpoints({ hostname: "::", port: 10100 }, { requestOrigin: "https://[2001:db8::1]:8443" }); + expect(result.audio).toEqual({ + transcriptionEndpoint: "https://[2001:db8::1]:8443/v1/audio/transcriptions", + dictationStreamEndpoint: "wss://[2001:db8::1]:8443/v1/audio/transcriptions/stream", + liveEndpoint: "wss://[2001:db8::1]:8443/v1/live", + realtimeCallsEndpoint: "https://[2001:db8::1]:8443/v1/realtime/calls", + transcriptionModel: "gpt-4o-transcribe", liveModel: "gpt-live-1-codex", + transcriptionConfigured: false, dictationConfigured: false, liveConfigured: false, + }); + const companion = buildApiAccessEndpoints({ hostname: "0.0.0.0", port: 10100, unauthenticatedLoopbackListener: { enabled: true, port: 10104 } }); + expect(companion.audio.liveEndpoint).toBe("ws://127.0.0.1:10104/v1/live"); + }); + + test("audio configuration distinguishes subscription, API key and noncanonical destinations", () => { + const forward = { adapter: "openai-responses" as const, baseUrl: "https://chatgpt.com/backend-api/codex" }; + const configured = buildApiAccessEndpoints({ providers: { openai: forward } }).audio; + expect(configured.transcriptionConfigured).toBe(true); + expect(configured.dictationConfigured).toBe(true); + expect(configured.liveConfigured).toBe(true); + for (const provider of [{ ...forward, disabled: true }, { ...forward, baseUrl: "https://example.test/v1" }, { ...forward, authMode: "key" as const }]) { + expect(buildApiAccessEndpoints({ providers: { openai: provider } }).audio.liveConfigured).toBe(false); + } + const api = buildApiAccessEndpoints({ providers: { "openai-apikey": { adapter: "openai-responses", baseUrl: "https://api.openai.com/v1", apiKey: "env:AUDIO_METADATA_FIXTURE" } } }).audio; + expect(api.transcriptionConfigured).toBe(true); + expect(api.dictationConfigured).toBe(false); + expect(api.liveConfigured).toBe(false); + expect(JSON.stringify(api)).not.toContain("AUDIO_METADATA_FIXTURE"); + }); }); describe("formatAuthorityHost", () => { From fb844790917a778a00fac3cd34138b9725c4fff9 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 19:44:43 +0900 Subject: [PATCH 2/9] fix(audio-ui): close review findings and remote build constraints --- .../030_connections.md | 1 + gui/src/audio-api-client.ts | 9 ++- gui/src/audio-api-examples.ts | 29 ++++++++++ .../apikeys-workspace/AudioApiPanel.tsx | 30 ++-------- gui/src/pages/api-keys-utils.ts | 2 + gui/src/styles-apikeys-workspace.css | 24 +++++++- gui/tests/audio-api-client.test.ts | 28 ++++++++++ gui/tests/audio-api-panel.test.tsx | 56 ++++++++++++++++++- 8 files changed, 148 insertions(+), 31 deletions(-) create mode 100644 gui/src/audio-api-examples.ts diff --git a/devlog/_plan/260912_audio_apis_stack/030_connections.md b/devlog/_plan/260912_audio_apis_stack/030_connections.md index e70417ddef..dd3bcac7f3 100644 --- a/devlog/_plan/260912_audio_apis_stack/030_connections.md +++ b/devlog/_plan/260912_audio_apis_stack/030_connections.md @@ -23,6 +23,7 @@ runs the checks; browser QA reads its built artifact with synthetic routes only. | MODIFY | gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx | place two unframed audio sections in existing Connections/API layout | | NEW | gui/src/components/apikeys-workspace/AudioApiPanel.tsx | accessible Dictation and Live Voice controls, endpoint/model display, sample copying, transient key/file controls and result/error states | | NEW | gui/src/audio-api-client.ts | bounded cancelable upload and socket client protocol helpers; no saved secrets | +| NEW | gui/src/audio-api-examples.ts | executable protocol examples outside component markup; localized prompt label supplied by caller | | MODIFY | gui/src/api.ts | narrow audio upload fetch entry bypasses management auth injection/recovery, validates exact inference path | | NEW | gui/tests/audio-api-client.test.ts | request generation, cancellation and protocol readiness tests | | NEW | gui/tests/audio-api-panel.test.tsx | real component upload, error, cancellation, missing metadata and deactivation flows | diff --git a/gui/src/audio-api-client.ts b/gui/src/audio-api-client.ts index 960fbf1841..17eefe2c56 100644 --- a/gui/src/audio-api-client.ts +++ b/gui/src/audio-api-client.ts @@ -3,7 +3,8 @@ import { createBoundedFetch } from "./bounded-fetch"; export type AudioErrorCode = "auth" | "unavailable" | "rateLimit" | "invalid" | "size" | "network" | "timeout" | "protocol"; export class AudioApiError extends Error { - constructor(readonly code: AudioErrorCode) { super(code); } + readonly code: AudioErrorCode; + constructor(code: AudioErrorCode) { super(code); this.code = code; } } export const AUDIO_FILE_MAX_BYTES = 25_000_000; @@ -128,7 +129,11 @@ export function connectLiveAudio(options: { if (message.type === "error" || message.type === "protocol.error") { finish("failed", "protocol"); return; } if (message.type === "session.started" || message.type === "session.updated") { const session = message.session; - if (session?.status === "closed" || session?.status === "error" || session?.status === "failed") { + if (session?.status === "error" || session?.status === "failed") { + finish("failed", "protocol"); + return; + } + if (session?.status === "closed") { finish(ready ? "disconnected" : "failed", ready ? undefined : "protocol"); return; } diff --git a/gui/src/audio-api-examples.ts b/gui/src/audio-api-examples.ts new file mode 100644 index 0000000000..c7580eddba --- /dev/null +++ b/gui/src/audio-api-examples.ts @@ -0,0 +1,29 @@ +import { LIVE_SESSION_UPDATE } from "./audio-api-client"; + +/** Executable protocol samples, not UI copy; credentials are supplied by the reader. */ +export function audioSocketExample(endpoint: string, live: boolean, model: string, keyLabel: string): string { + const url = new URL(endpoint); + if (live) url.searchParams.set("model", model); + const start = live ? LIVE_SESSION_UPDATE + : { type: "session.start", config: { input_audio_format: "pcm16", sample_rate_hz: 48000, num_channels: 1, max_buffer_size_bytes: 4194304, max_utterance_duration_ms: 30000, session_ttl_ms: 300000, provider_mode: "streaming_sse", transcript_delivery_mode: "segment", vad: { type: "server_vad", threshold: 0.5, prefix_padding_ms: 300, silence_duration_ms: 500 } } }; + return `const key = prompt(${JSON.stringify(keyLabel)})?.trim(); +if (!key) throw new Error("missing_data_key"); +const encoded = btoa(String.fromCharCode(...new TextEncoder().encode(key))) + .replace(/\\+/g, "-").replace(/\\//g, "_").replace(/=+$/, ""); +const ws = new WebSocket(${JSON.stringify(url.href)}, + ["opencodex-audio", "opencodex-key." + encoded]); +ws.onopen = () => ws.send(JSON.stringify(${JSON.stringify(start)})); +ws.onmessage = ({ data }) => console.log(JSON.parse(data).type); +// After session.started, send your protocol's audio frames. +// Dictation: mono PCM16 at the declared sample_rate_hz (48000 here). +// Live Voice: input_audio.append / output_audio.delta, 24 kHz mono. +const closeSession = () => ws.send(JSON.stringify({ type: "session.close" }));`; +} + +export function audioUploadExample(endpoint: string, model: string): string { + return [ + `curl ${JSON.stringify(endpoint)}`, + ' -H "X-OpenCodex-API-Key: $OPENCODEX_API_KEY"', + ` -F "file=@recording.wav" -F "model=${model}"`, + ].join(" \\\n"); +} diff --git a/gui/src/components/apikeys-workspace/AudioApiPanel.tsx b/gui/src/components/apikeys-workspace/AudioApiPanel.tsx index 6599fb3c65..06596fa372 100644 --- a/gui/src/components/apikeys-workspace/AudioApiPanel.tsx +++ b/gui/src/components/apikeys-workspace/AudioApiPanel.tsx @@ -4,25 +4,7 @@ import { IconLink, IconPlay, IconX } from "../../icons"; import { useT } from "../../i18n/shared"; import { CopyableExample, EndpointUrl } from "../../pages/api-keys-copy"; import type { AudioApiInfo } from "../../pages/api-keys-utils"; - -function socketSample(endpoint: string, live: boolean, model: string): string { - const url = new URL(endpoint); - if (live) url.searchParams.set("model", model); - const start = live - ? { type: "session.update", session: { instructions: "", audio: { output: { voice: "cove" } }, delegation: { type: "client" } } } - : { type: "session.start", config: { input_audio_format: "pcm16", sample_rate_hz: 48000, num_channels: 1, max_buffer_size_bytes: 4194304, max_utterance_duration_ms: 30000, session_ttl_ms: 300000, provider_mode: "streaming_sse", transcript_delivery_mode: "segment", vad: { type: "server_vad", threshold: 0.5, prefix_padding_ms: 300, silence_duration_ms: 500 } } }; - return `const key = prompt("OpenCodex data key").trim(); -const encoded = btoa(String.fromCharCode(...new TextEncoder().encode(key))) - .replace(/\\+/g, "-").replace(/\\//g, "_").replace(/=+$/, ""); -const ws = new WebSocket(${JSON.stringify(url.href)}, - ["opencodex-audio", "opencodex-key." + encoded]); -ws.onopen = () => ws.send(JSON.stringify(${JSON.stringify(start)})); -ws.onmessage = ({ data }) => console.log(JSON.parse(data).type); -// After session.started, send your protocol's audio frames. -// Dictation: mono PCM16 at the declared sample_rate_hz (48000 here). -// Live Voice: input_audio.append / output_audio.delta, 24 kHz mono. -const closeSession = () => ws.send(JSON.stringify({ type: "session.close" }));`; -} +import { audioSocketExample, audioUploadExample } from "../../audio-api-examples"; function AudioKeyInput({ value, onChange }: { value: string; onChange: (value: string) => void }) { const t = useT(); @@ -96,14 +78,10 @@ export function DictationPanel({ audio }: { audio?: AudioApiInfo }) { {text !== null &&

{t("audio.transcript")}

{text ? :

{t("audio.emptyTranscript")}

}
}
{t("audio.examples")} - +

{t("audio.streaming")}

{t(audio.dictationConfigured ? "audio.configured" : "audio.notConfigured")}
- +
} ; @@ -156,7 +134,7 @@ export function LiveVoicePanel({ audio }: { audio?: AudioApiInfo }) { {events.length > 0 &&
{events.join("\n")}
} -
{t("audio.examples")}
+
{t("audio.examples")}
} ; } diff --git a/gui/src/pages/api-keys-utils.ts b/gui/src/pages/api-keys-utils.ts index 980f244f9a..a687f1ea69 100644 --- a/gui/src/pages/api-keys-utils.ts +++ b/gui/src/pages/api-keys-utils.ts @@ -88,6 +88,8 @@ export interface AudioApiInfo { export function isAudioApiInfo(value: unknown, baseUrl: string): value is AudioApiInfo { if (!value || typeof value !== "object" || Array.isArray(value)) return false; const audio = value as Record; + const fields = new Set(["transcriptionEndpoint", "dictationStreamEndpoint", "liveEndpoint", "realtimeCallsEndpoint", "transcriptionModel", "liveModel", "transcriptionConfigured", "dictationConfigured", "liveConfigured"]); + if (Object.keys(audio).some(field => !fields.has(field))) return false; try { const base = new URL(baseUrl); if (!["http:", "https:"].includes(base.protocol) || base.username || base.password || base.search || base.hash) return false; diff --git a/gui/src/styles-apikeys-workspace.css b/gui/src/styles-apikeys-workspace.css index dbe47c1d94..0e9cb9f3a6 100644 --- a/gui/src/styles-apikeys-workspace.css +++ b/gui/src/styles-apikeys-workspace.css @@ -107,7 +107,29 @@ .audio-api-actions .btn { min-height: 36px; white-space: nowrap; } .audio-api-status { font-size: var(--text-label); } .audio-api-error { margin: 0; color: var(--danger); font-size: var(--text-label); } -.audio-api-endpoints .api-endpoint-url { white-space: normal; overflow-wrap: anywhere; } +.audio-api-section .api-endpoint-url-btn { + display: block; + width: 100%; + max-width: 100%; + min-width: 0; + text-align: left; + appearance: none; + border: 0; + background: transparent; + padding: 0; + margin: 0; + color: inherit; + font: inherit; + cursor: pointer; +} +.audio-api-section .api-endpoint-url { + display: block; + box-sizing: border-box; + width: 100%; + max-width: 100%; + white-space: normal; + overflow-wrap: anywhere; +} .audio-api-examples { min-width: 0; } .audio-api-examples summary { cursor: pointer; font-size: var(--text-label); padding: 6px 0; } .audio-api-examples > :not(summary) { margin-top: var(--space-2); } diff --git a/gui/tests/audio-api-client.test.ts b/gui/tests/audio-api-client.test.ts index 1d0aa25fbc..43ebfa727d 100644 --- a/gui/tests/audio-api-client.test.ts +++ b/gui/tests/audio-api-client.test.ts @@ -2,6 +2,7 @@ import { afterEach, expect, test } from "bun:test"; import { AUDIO_FILE_MAX_BYTES, AudioApiError, LIVE_SESSION_UPDATE, audioSocketProtocols, connectLiveAudio, transcribeAudio } from "../src/audio-api-client"; import { resetApiAuthFetchForTests } from "../src/api"; import { isAudioApiInfo, type AudioApiInfo } from "../src/pages/api-keys-utils"; +import { audioSocketExample, audioUploadExample } from "../src/audio-api-examples"; const originalFetch = globalThis.fetch; const originalSocket = globalThis.WebSocket; @@ -71,6 +72,7 @@ test("audio metadata accepts the exact scheme/origin/path projection only", () = } expect(isAudioApiInfo(undefined, base)).toBe(false); expect(isAudioApiInfo({ ...audio, liveConfigured: "true" }, base)).toBe(false); + expect(isAudioApiInfo({ ...audio, apiKey: "sensitive-marker" }, base)).toBe(false); }); class FakeSocket { @@ -143,3 +145,29 @@ test("voice readiness and established-session timers release all handlers", asyn expect(ready.states).toEqual(["connecting", "connected", "disconnected"]); expect(ready.socket.closed).toBe(true); }); + +test("failed session updates remain failures even after readiness", () => { + for (const status of ["error", "failed"]) { + const { socket, states, dispose } = probe(); + socket.open(); socket.message({ type: "session.started", session: { id: "fixture" } }); + const oldClose = socket.onclose; + socket.message({ type: "session.updated", session: { id: "fixture", status } }); + oldClose?.({ code: 1000 }); + expect(states).toEqual(["connecting", "connected", "failed:protocol"]); + expect(socket.closed).toBe(true); + dispose(); + } +}); + +test("copied socket examples are executable protocol code with a localized key prompt", () => { + const example = audioSocketExample("wss://gateway.example/v1/live", true, "gpt-live-1-codex", "데이터 키"); + let label = ""; + const run = new Function("WebSocket", "prompt", "btoa", "TextEncoder", example + "; return ws;"); + const socket = run(FakeSocket, (value: string) => { label = value; return KEY; }, btoa, TextEncoder) as FakeSocket; + expect(label).toBe("데이터 키"); + expect(socket.protocols).toEqual(audioSocketProtocols(KEY)); + socket.open(); + expect(JSON.parse(socket.sent[0]!)).toEqual(LIVE_SESSION_UPDATE); + expect(audioUploadExample(ENDPOINT, "gpt-4o-transcribe")).toContain("$OPENCODEX_API_KEY"); + expect(audioUploadExample(ENDPOINT, "gpt-4o-transcribe")).not.toContain("\n+"); +}); diff --git a/gui/tests/audio-api-panel.test.tsx b/gui/tests/audio-api-panel.test.tsx index fda94960db..40a2018e18 100644 --- a/gui/tests/audio-api-panel.test.tsx +++ b/gui/tests/audio-api-panel.test.tsx @@ -82,10 +82,10 @@ async function render(active = true, apiBase = "http://localhost") { await act(async () => { root!.render(); }); await flush(); } -async function typeKey(section: string) { +async function typeKey(section: string, value = KEY) { const input = container.querySelector(`${section} input[type=password]`)!; await act(async () => { - Object.getOwnPropertyDescriptor(win.HTMLInputElement.prototype, "value")!.set!.call(input, KEY); + Object.getOwnPropertyDescriptor(win.HTMLInputElement.prototype, "value")!.set!.call(input, value); input.dispatchEvent(new win.Event("input", { bubbles: true }) as unknown as Event); }); } @@ -159,3 +159,55 @@ test("missing or malformed audio metadata leaves existing key management usable" expect(container.textContent).toContain("Generate"); expect(sent).toHaveLength(0); }); + +test("Cancel, key replacement and duplicate clicks cannot publish a superseded upload", async () => { + const replies: Array<(response: Response) => void> = []; + responder = () => new Promise(resolve => { replies.push(resolve); }); + await render(); await typeKey(DICTATION); await selectFile(); + await act(async () => { + const button = container.querySelector(`${DICTATION} button[type=submit]`)!; + button.click(); button.click(); + }); + await flush(); + expect(sent).toHaveLength(1); + await act(async () => { container.querySelector(`${DICTATION} .audio-api-actions button[type=button]`)!.click(); }); + expect(sent[0]!.signal!.aborted).toBe(true); + await submit(DICTATION); + expect(sent).toHaveLength(2); + await typeKey(DICTATION, KEY + "-replacement"); + expect(sent[1]!.signal!.aborted).toBe(true); + await submit(DICTATION); + expect(sent).toHaveLength(3); + await act(async () => { replies[0]!(Response.json({ text: "stale A" })); replies[1]!(Response.json({ text: "stale B" })); }); + expect(container.textContent).not.toContain("stale A"); expect(container.textContent).not.toContain("stale B"); + await act(async () => { replies[2]!(Response.json({ text: "Current C" })); }); + expect(container.querySelector(".audio-api-result")?.textContent).toContain("Current C"); +}); + +test("origin changes close the prior socket and clear the transient key", async () => { + await render(); await typeKey(LIVE); await submit(LIVE); + const socket = Socket.latest!; + await render(true, "http://127.0.0.1"); + expect(socket.closed).toBe(true); + expect(container.querySelector(`${LIVE} input[type=password]`)!.value).toBe(""); +}); + +test("unknown fields cannot write secrets to the list cache", async () => { + audio = { ...AUDIO, apiKey: "sensitive-extra-marker" }; + await render(); + expect(container.querySelector(`${DICTATION} input`)).toBeNull(); + expect(win.sessionStorage.getItem("ocx.apikeys.list.v2:http://localhost")).not.toContain("sensitive-extra-marker"); +}); + +test("old server and malformed cache audio never invent configured support", async () => { + audio = undefined; + win.sessionStorage.setItem("ocx.apikeys.list.v2:http://localhost", JSON.stringify({ + keys: [], claudeCodeEnabled: true, + authMatrix: [{ endpoint: "/v1/models", bearer: "accepted", dedicated: "accepted", xApiKey: "accepted" }], + endpoints: { baseUrl: BASE, responses: `${BASE}/responses`, chatCompletions: `${BASE}/chat/completions`, messages: `${BASE}/messages`, models: `${BASE}/models`, audio: { ...AUDIO, liveConfigured: "yes" } }, + })); + await render(); + expect(container.querySelector(DICTATION)?.textContent).toContain("Audio metadata unavailable"); + expect(container.querySelector(`${LIVE} input`)).toBeNull(); + expect(sent).toHaveLength(0); +}); From 695670e69cf8ef312ffee4eb52453d20f9ff5cc5 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 19:49:09 +0900 Subject: [PATCH 3/9] test(audio-ui): observe pending cache and replacement states --- gui/tests/audio-api-panel.test.tsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/gui/tests/audio-api-panel.test.tsx b/gui/tests/audio-api-panel.test.tsx index 40a2018e18..a08b67e40f 100644 --- a/gui/tests/audio-api-panel.test.tsx +++ b/gui/tests/audio-api-panel.test.tsx @@ -24,6 +24,7 @@ let container: HTMLDivElement; let audio: unknown; let sent: RequestInit[]; let responder: (init: RequestInit) => Promise; +let holdKeys: Promise | null; class Socket { static OPEN = 1; @@ -49,11 +50,14 @@ beforeEach(() => { Object.defineProperty(globalThis, "IS_REACT_ACT_ENVIRONMENT", { configurable: true, value: true }); Object.defineProperty(globalThis, "WebSocket", { configurable: true, writable: true, value: Socket }); win.localStorage.setItem("ocx-lang", "en"); - audio = AUDIO; sent = []; Socket.latest = undefined; + audio = AUDIO; sent = []; Socket.latest = undefined; holdKeys = null; responder = async () => Response.json({ text: "Synthetic transcript" }); Object.defineProperty(globalThis, "fetch", { configurable: true, writable: true, value: async (input: RequestInfo | URL, init?: RequestInit) => { const url = String(input); - if (url.endsWith("/api/keys")) return Response.json({ keys: [], baseUrl: BASE, endpoint: `${BASE}/responses`, authMatrix: [{ endpoint: "/v1/models", bearer: "accepted", dedicated: "accepted", xApiKey: "accepted" }], ...(audio === undefined ? {} : { audio }) }); + if (url.endsWith("/api/keys")) { + if (holdKeys) await holdKeys; + return Response.json({ keys: [], baseUrl: BASE, endpoint: `${BASE}/responses`, authMatrix: [{ endpoint: "/v1/models", bearer: "accepted", dedicated: "accepted", xApiKey: "accepted" }], ...(audio === undefined ? {} : { audio }) }); + } if (url.endsWith("/v1/models")) return Response.json({ data: [] }); if (url.endsWith("/v1/audio/transcriptions")) { sent.push(init!); return responder(init!); } return new Response(null, { status: 404 }); @@ -180,6 +184,10 @@ test("Cancel, key replacement and duplicate clicks cannot publish a superseded u expect(sent).toHaveLength(3); await act(async () => { replies[0]!(Response.json({ text: "stale A" })); replies[1]!(Response.json({ text: "stale B" })); }); expect(container.textContent).not.toContain("stale A"); expect(container.textContent).not.toContain("stale B"); + expect(container.querySelector(`${DICTATION} button[type=submit]`)!.disabled).toBe(true); + expect(container.querySelector(`${DICTATION} .audio-api-actions button[type=button]`)?.textContent).toContain("Cancel"); + await submit(DICTATION); + expect(sent).toHaveLength(3); await act(async () => { replies[2]!(Response.json({ text: "Current C" })); }); expect(container.querySelector(".audio-api-result")?.textContent).toContain("Current C"); }); @@ -201,6 +209,8 @@ test("unknown fields cannot write secrets to the list cache", async () => { test("old server and malformed cache audio never invent configured support", async () => { audio = undefined; + let release!: () => void; + holdKeys = new Promise(resolve => { release = resolve; }); win.sessionStorage.setItem("ocx.apikeys.list.v2:http://localhost", JSON.stringify({ keys: [], claudeCodeEnabled: true, authMatrix: [{ endpoint: "/v1/models", bearer: "accepted", dedicated: "accepted", xApiKey: "accepted" }], @@ -209,5 +219,9 @@ test("old server and malformed cache audio never invent configured support", asy await render(); expect(container.querySelector(DICTATION)?.textContent).toContain("Audio metadata unavailable"); expect(container.querySelector(`${LIVE} input`)).toBeNull(); + expect(container.textContent).toContain("Generate"); + release(); + await flush(); + expect(container.querySelector(`${LIVE} input`)).toBeNull(); expect(sent).toHaveLength(0); }); From 2b2aed441bbb1d6530ec8de57181106153c148fa Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 19:53:09 +0900 Subject: [PATCH 4/9] fix(audio-ui): keep mobile section navigation below the app bar --- gui/src/styles-apikeys-workspace.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/gui/src/styles-apikeys-workspace.css b/gui/src/styles-apikeys-workspace.css index 0e9cb9f3a6..f082f76ba0 100644 --- a/gui/src/styles-apikeys-workspace.css +++ b/gui/src/styles-apikeys-workspace.css @@ -815,3 +815,16 @@ padding: var(--space-2) 0 0; } } + +/* The shell's mobile top bar is 44px controls + 8px padding + 1px border. */ +@media (max-width: 760px) { + .apikeys-workspace-shell .section-tabs { + top: 53px; + flex-wrap: nowrap; + overflow-x: auto; + overflow-y: hidden; + padding-top: 0; + min-height: 44px; + } + .apikeys-workspace-shell .awi-section-anchor { scroll-margin-top: 108px; } +} From 6899446d51c93054df4e23db33ed67d3b11a9381 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 19:56:46 +0900 Subject: [PATCH 5/9] docs(audio-ui): clarify local data-key requirements --- gui/src/i18n/de.ts | 2 +- gui/src/i18n/en.ts | 2 +- gui/src/i18n/fr.ts | 2 +- gui/src/i18n/ja.ts | 2 +- gui/src/i18n/ko.ts | 2 +- gui/src/i18n/ru.ts | 2 +- gui/src/i18n/tr.ts | 2 +- gui/src/i18n/zh-TW.ts | 2 +- gui/src/i18n/zh.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index dea707e149..bde60bbf6c 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -1585,7 +1585,7 @@ export const de: Record = { "api.endpointsTitle": "Gateway-Endpunkte", "api.authBaseUrlNote": "Konfiguriere Clients mit der Basis-URL und wähle dann den protokollspezifischen Endpunkt unten.", "api.authTitle": "Authentifizierung", - "api.authLoopback": "Loopback-Binds (127.0.0.1 oder ::1) umgehen die Authentifizierung. Remote-Binds benötigen einen generierten ocx_-Schlüssel oder OPENCODEX_API_AUTH_TOKEN.", + "api.authLoopback": "Loopback-Zugriff hängt von der Route ab; eigenständige Audio-Clients benötigen einen OpenCodex-Datenschlüssel. Remote-Zugriff erfordert einen Datenschlüssel oder OPENCODEX_API_AUTH_TOKEN.", "api.modelsTitle": "Externe Modelle", "api.modelsCount": "{count} aufrufbar", "api.modelsSearch": "Modelle suchen", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index 50e709f16d..7c56acff69 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -2171,7 +2171,7 @@ export const en = { "api.endpointNote": "Use the base URL with OpenAI-compatible clients. Responses and Chat Completions are exposed under /v1.", "api.endpointsTitle": "Endpoints", "api.authTitle": "Authentication", - "api.authLoopback": "Loopback binds (127.0.0.1 or ::1) bypass authentication. Remote binds require a generated ocx_ key or OPENCODEX_API_AUTH_TOKEN.", + "api.authLoopback": "Loopback access is route-specific; standalone audio clients require an OpenCodex data key. Remote binds require a data key or OPENCODEX_API_AUTH_TOKEN.", "api.authBaseUrlNote": "Configure clients with the base URL, then choose the protocol-specific endpoint below.", "api.newKeyTitle": "New key created", "api.newKeyNote": "Copy this key now — it won't be shown again.", diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index b448dc37e4..12b4353bf5 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -2091,7 +2091,7 @@ export const fr: Record = { "api.endpointNote": "Utilisez l’URL de base avec les clients compatibles avec OpenAI. Responses et Chat Completions sont accessibles sous /v1.", "api.endpointsTitle": "Points de terminaison", "api.authTitle": "Authentification", - "api.authLoopback": "Les écoutes en boucle locale (127.0.0.1 ou ::1) contournent l’authentification. Les écoutes distantes nécessitent une clé ocx_ générée ou OPENCODEX_API_AUTH_TOKEN.", + "api.authLoopback": "L’accès local dépend de la route ; les clients audio autonomes nécessitent une clé de données OpenCodex. L’accès distant nécessite une clé de données ou OPENCODEX_API_AUTH_TOKEN.", "api.authBaseUrlNote": "Configurez les clients avec l’URL de base, puis choisissez ci-dessous le point de terminaison propre au protocole.", "api.newKeyTitle": "Nouvelle clé créée", "api.newKeyNote": "Copiez cette clé maintenant — elle ne sera plus affichée.", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 24bfae9d58..b8119052e1 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -2023,7 +2023,7 @@ export const ja: Record = { "api.modelsEndpoint": "Models API", "api.authTitle": "認証", "api.authBaseUrlNote": "クライアントにはベース URL を設定し、下のプロトコル別エンドポイントを選んでください。", - "api.authLoopback": "ループバック (127.0.0.1 または ::1) は認証を省略します。リモートでは生成した ocx_ キーまたは OPENCODEX_API_AUTH_TOKEN が必要です。", + "api.authLoopback": "ループバックの認証は経路によって異なります。独立した音声クライアントにはOpenCodexデータキーが必要です。リモート接続にはデータキーまたはOPENCODEX_API_AUTH_TOKENが必要です。", "api.modelsTitle": "外部モデルカタログ", "api.modelsCount": "{count} 件が利用可能", "api.modelsSearch": "モデルを検索", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index c712e64424..5a031a9698 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -1624,7 +1624,7 @@ export const ko: Record = { "api.endpointsTitle": "게이트웨이 엔드포인트", "api.authBaseUrlNote": "클라이언트에는 기본 URL을 설정한 뒤 아래에서 프로토콜별 엔드포인트를 선택하세요.", "api.authTitle": "인증", - "api.authLoopback": "루프백 바인드(127.0.0.1 또는 ::1)는 인증을 건너뜁니다. 원격 바인드는 생성된 ocx_ 키 또는 OPENCODEX_API_AUTH_TOKEN이 필요합니다.", + "api.authLoopback": "루프백 인증은 경로마다 다릅니다. 독립 음성 API에는 OpenCodex 데이터 키가 필요합니다. 원격 연결에는 데이터 키 또는 OPENCODEX_API_AUTH_TOKEN이 필요합니다.", "api.modelsTitle": "외부 모델 카탈로그", "api.modelsCount": "{count}개 호출 가능", "api.modelsSearch": "모델 검색", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index c4a3ae32c6..609fe25daf 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -2093,7 +2093,7 @@ export const ru: Record = { "api.endpointsTitle": "Конечные точки", "api.authTitle": "Аутентификация", "api.authBaseUrlNote": "Настройте клиентов с базовым URL, затем выберите нужный протокольный endpoint ниже.", - "api.authLoopback": "Loopback-привязки (127.0.0.1 или ::1) обходят аутентификацию. Для удалённых привязок нужен сгенерированный ocx_-ключ или OPENCODEX_API_AUTH_TOKEN.", + "api.authLoopback": "Доступ через loopback зависит от маршрута; отдельным аудиоклиентам нужен ключ данных OpenCodex. Для удалённого доступа нужен ключ данных или OPENCODEX_API_AUTH_TOKEN.", "api.modelsTitle": "Каталог внешних моделей", "api.modelsCount": "{count} доступно", "api.modelsSearch": "Поиск моделей", diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index b17b909e99..755aa8b48c 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -2112,7 +2112,7 @@ export const tr: Record = { "api.endpointNote": "OpenAI uyumlu istemcilerle taban URL'yi kullanın.", "api.endpointsTitle": "Uç noktalar", "api.authTitle": "Kimlik Doğrulama", - "api.authLoopback": "Geri döngü (loopback) bağlantıları (127.0.0.1 / ::1) kimlik doğrulamasını atlar. Harici/ağ istemcileri x-opencodex-api-key veya Authorization başlığında bir ocx_ API anahtarı ya da OPENCODEX_API_AUTH_TOKEN göndermelidir.", + "api.authLoopback": "Geri döngü erişimi yola bağlıdır; bağımsız ses istemcileri OpenCodex veri anahtarı gerektirir. Uzak bağlantılar veri anahtarı veya OPENCODEX_API_AUTH_TOKEN gerektirir.", "api.authBaseUrlNote": "İstemcileri taban URL ile yapılandırın.", "api.newKeyTitle": "Yeni anahtar oluşturuldu", "api.newKeyNote": "Bu anahtarı şimdi kopyalayın — tekrar gösterilmeyecektir.", diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index 723f54b1e1..17b483dcd3 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -1630,7 +1630,7 @@ export const zhTW: Record = { "api.endpointNote": "請將基礎 URL 用於 OpenAI 相容客戶端。Responses 與 Chat Completions 在 /v1 下提供。", "api.endpointsTitle": "閘道器端點", "api.authTitle": "身份驗證", - "api.authLoopback": "迴環繫結(127.0.0.1 或 ::1)會跳過身份驗證。遠端繫結需要生成的 ocx_ 金鑰或 OPENCODEX_API_AUTH_TOKEN。", + "api.authLoopback": "迴環存取的驗證要求依路徑而異;獨立音訊用戶端需要 OpenCodex 資料金鑰。遠端連線需要資料金鑰或 OPENCODEX_API_AUTH_TOKEN。", "api.authBaseUrlNote": "客戶端應使用基礎 URL,然後選擇下面的協議端點。", "api.newKeyTitle": "已建立新金鑰", "api.newKeyNote": "請立即複製此金鑰,它不會再次顯示。", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index c36380c0d7..af1acbbdab 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -1605,7 +1605,7 @@ export const zh: Record = { "api.endpointsTitle": "网关端点", "api.authBaseUrlNote": "客户端应使用基础 URL,然后选择下面的协议端点。", "api.authTitle": "身份验证", - "api.authLoopback": "回环绑定(127.0.0.1 或 ::1)会跳过身份验证。远程绑定需要生成的 ocx_ 密钥或 OPENCODEX_API_AUTH_TOKEN。", + "api.authLoopback": "回环访问的认证要求因路径而异;独立音频客户端需要 OpenCodex 数据密钥。远程连接需要数据密钥或 OPENCODEX_API_AUTH_TOKEN。", "api.modelsTitle": "外部模型目录", "api.modelsCount": "{count} 个可调用", "api.modelsSearch": "搜索模型", From f09195296064bff53a5403f6160fc79fc086ab45 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 20:00:13 +0900 Subject: [PATCH 6/9] fix(audio-ui): bound section strip height at tablet widths --- gui/src/styles-apikeys-workspace.css | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/gui/src/styles-apikeys-workspace.css b/gui/src/styles-apikeys-workspace.css index f082f76ba0..ce9fa08654 100644 --- a/gui/src/styles-apikeys-workspace.css +++ b/gui/src/styles-apikeys-workspace.css @@ -816,15 +816,16 @@ } } +.apikeys-workspace-shell .section-tabs { + flex-wrap: nowrap; + overflow-x: auto; + overflow-y: hidden; + padding-top: 0; + min-height: 44px; +} + /* The shell's mobile top bar is 44px controls + 8px padding + 1px border. */ @media (max-width: 760px) { - .apikeys-workspace-shell .section-tabs { - top: 53px; - flex-wrap: nowrap; - overflow-x: auto; - overflow-y: hidden; - padding-top: 0; - min-height: 44px; - } + .apikeys-workspace-shell .section-tabs { top: 53px; } .apikeys-workspace-shell .awi-section-anchor { scroll-margin-top: 108px; } } From fca6a78bf366922c3c5c6120d2da693c6cb10e24 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 20:05:55 +0900 Subject: [PATCH 7/9] fix(audio-ui): align mobile scroll state and idle voice status --- .../apikeys-workspace/ApiKeysWorkspace.tsx | 2 +- .../apikeys-workspace/AudioApiPanel.tsx | 1 + gui/src/components/section-tabs.tsx | 19 +++++++++++--- gui/tests/audio-api-panel.test.tsx | 4 ++- gui/tests/section-tabs-scroll-lock.test.tsx | 26 ++++++++++++++++--- structure/gui-and-management-api.md | 3 +++ 6 files changed, 46 insertions(+), 9 deletions(-) diff --git a/gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx b/gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx index df3bf1974a..ff885ff616 100644 --- a/gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx +++ b/gui/src/components/apikeys-workspace/ApiKeysWorkspace.tsx @@ -250,7 +250,7 @@ export default function ApiKeysWorkspace({ one, the pattern Usage / Logs / Subagents already use. A rail plus a content pane was a second vertical band competing for the same width, and at 1280px it cost the content column 252px it could not spare. */} - {!selected && } + {!selected && }
{selected ? ( diff --git a/gui/src/components/apikeys-workspace/AudioApiPanel.tsx b/gui/src/components/apikeys-workspace/AudioApiPanel.tsx index 06596fa372..4509062f31 100644 --- a/gui/src/components/apikeys-workspace/AudioApiPanel.tsx +++ b/gui/src/components/apikeys-workspace/AudioApiPanel.tsx @@ -97,6 +97,7 @@ export function LiveVoicePanel({ audio }: { audio?: AudioApiInfo }) { const id = useId(); useEffect(() => () => { connection.current?.dispose?.(); connection.current = null; }, []); const disconnect = () => { + if (!connection.current) return; connection.current?.dispose?.(); connection.current = null; setState("disconnected"); }; diff --git a/gui/src/components/section-tabs.tsx b/gui/src/components/section-tabs.tsx index fd5999bba4..7519f7135a 100644 --- a/gui/src/components/section-tabs.tsx +++ b/gui/src/components/section-tabs.tsx @@ -22,11 +22,23 @@ export function SectionTabs({ scope, items, ariaLabel, + mobileReadingLine, }: { scope: string; items: SectionTabItem[]; ariaLabel: string; + /** Optional offset for a shell with a mobile top bar above its section strip. */ + mobileReadingLine?: number; }) { + const [readingLine, setReadingLine] = useState(() => mobileReadingLine && window.matchMedia("(max-width: 760px)").matches ? mobileReadingLine : 72); + useEffect(() => { + if (!mobileReadingLine) return; + const query = window.matchMedia("(max-width: 760px)"); + const update = () => setReadingLine(query.matches ? mobileReadingLine : 72); + update(); + query.addEventListener("change", update); + return () => query.removeEventListener("change", update); + }, [mobileReadingLine]); const [active, setActive] = useState(items[0]?.id ?? ""); /** While set, scroll-spy ignores intermediate sections during smooth scroll-to-click. */ const scrollLockRef = useRef(null); @@ -49,7 +61,6 @@ export function SectionTabs({ // so a destination that stopped mid-viewport still wins over an off-screen prior heading. let bestId: string | null = null; let bestDistance = Number.POSITIVE_INFINITY; - const readingLine = 72; for (const item of items) { const node = document.getElementById(sectionAnchorId(scope, item.id)); if (!node) continue; @@ -60,7 +71,7 @@ export function SectionTabs({ } } if (bestId) setActive(bestId); - }, [clearScrollLock, items, scope]); + }, [clearScrollLock, items, scope, readingLine]); useEffect(() => () => clearScrollLock(), [clearScrollLock]); @@ -92,11 +103,11 @@ export function SectionTabs({ const id = visible.target.id.slice(sectionAnchorPrefix(scope).length); setActive(current => (current === id ? current : id)); }, - { rootMargin: "-72px 0px -60% 0px", threshold: 0 }, + { rootMargin: `-${readingLine}px 0px -60% 0px`, threshold: 0 }, ); for (const node of nodes) observer.observe(node); return () => observer.disconnect(); - }, [clearScrollLock, items, scope]); + }, [clearScrollLock, items, scope, readingLine]); const go = (id: string) => { const target = document.getElementById(sectionAnchorId(scope, id)); diff --git a/gui/tests/audio-api-panel.test.tsx b/gui/tests/audio-api-panel.test.tsx index a08b67e40f..bbffa6a45b 100644 --- a/gui/tests/audio-api-panel.test.tsx +++ b/gui/tests/audio-api-panel.test.tsx @@ -143,7 +143,9 @@ test("upload errors are localized, cancellation and inactive panels cannot publi test("voice reports real readiness, filters event payloads and releases on deactivation", async () => { await render(); expect(Socket.latest).toBeUndefined(); - await typeKey(LIVE); await submit(LIVE); + await typeKey(LIVE); + expect(container.querySelector(`${LIVE} [role=status]`)?.textContent).toBe("Not checked"); + await submit(LIVE); const socket = Socket.latest!; expect(socket).toBeDefined(); await act(async () => { socket.readyState = 1; socket.onopen?.(); }); diff --git a/gui/tests/section-tabs-scroll-lock.test.tsx b/gui/tests/section-tabs-scroll-lock.test.tsx index 6f54bb818a..26bbdcfcc4 100644 --- a/gui/tests/section-tabs-scroll-lock.test.tsx +++ b/gui/tests/section-tabs-scroll-lock.test.tsx @@ -9,7 +9,7 @@ const globals = ["document", "window", "navigator", "IS_REACT_ACT_ENVIRONMENT"] let previousGlobals: Record<(typeof globals)[number], unknown>; let testWindow: Window; -const observers: Array<{ callback: IntersectionObserverCallback; nodes: Element[] }> = []; +const observers: Array<{ callback: IntersectionObserverCallback; nodes: Element[]; rootMargin?: string }> = []; const OriginalIntersectionObserver = globalThis.IntersectionObserver; function emitIntersecting(id: string) { @@ -42,9 +42,9 @@ beforeEach(() => { #callback: IntersectionObserverCallback; #nodes: Element[] = []; - constructor(callback: IntersectionObserverCallback) { + constructor(callback: IntersectionObserverCallback, options?: IntersectionObserverInit) { this.#callback = callback; - observers.push({ callback: this.#callback.bind(this), nodes: this.#nodes }); + observers.push({ callback: this.#callback.bind(this), nodes: this.#nodes, rootMargin: options?.rootMargin }); } observe(node: Element) { @@ -70,6 +70,26 @@ beforeEach(() => { }); }); +test("a mobile shell offset updates the observer without changing default section behavior", async () => { + const original = window.matchMedia; + let listener: (() => void) | undefined; + const query = { matches: true, addEventListener: (_name: string, callback: () => void) => { listener = callback; }, removeEventListener: () => { listener = undefined; } }; + window.matchMedia = (() => query) as unknown as typeof window.matchMedia; + const container = document.createElement("div"); document.body.append(container); + const { createRoot } = await import("react-dom/client"); + const root = createRoot(container); + try { + await act(async () => root.render(<>
)); + expect(observers.at(-1)?.rootMargin).toBe("-108px 0px -60% 0px"); + await act(async () => { query.matches = false; listener?.(); }); + expect(observers.at(-1)?.rootMargin).toBe("-72px 0px -60% 0px"); + } finally { + await act(async () => root.unmount()); + expect(listener).toBeUndefined(); + window.matchMedia = original; container.remove(); + } +}); + afterEach(() => { Object.defineProperty(globalThis, "IntersectionObserver", { configurable: true, diff --git a/structure/gui-and-management-api.md b/structure/gui-and-management-api.md index 78c3f6897a..379bb7a0b1 100644 --- a/structure/gui-and-management-api.md +++ b/structure/gui-and-management-api.md @@ -309,6 +309,9 @@ requests/sockets. Only allowlisted event types and localized error categories ar displayed. Tests live in `gui/tests/audio-api-client.test.ts`, `gui/tests/audio-api-panel.test.tsx`, `gui/tests/api-auth-memory.test.ts` and `tests/server/api-access-endpoints.test.ts`. +The API workspace gives `gui/src/components/section-tabs.tsx` its mobile reading +line so scroll-spy and the top-bar offset agree; other consumers keep their +existing reading line. The section strip stays one row at every width. Provider Overview consumes the existing shared `add-provider-presets` resource for sponsor presentation. `matchingWorkspacePreset` requires the configured id, adapter and normalized From f5aefd88af3116bec4f2ddc72c0c6fa974f52a83 Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 20:07:54 +0900 Subject: [PATCH 8/9] fix(audio-ui): keep observer geometry outside text interpolation --- gui/src/components/section-tabs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/components/section-tabs.tsx b/gui/src/components/section-tabs.tsx index 7519f7135a..0866ec42b6 100644 --- a/gui/src/components/section-tabs.tsx +++ b/gui/src/components/section-tabs.tsx @@ -103,7 +103,7 @@ export function SectionTabs({ const id = visible.target.id.slice(sectionAnchorPrefix(scope).length); setActive(current => (current === id ? current : id)); }, - { rootMargin: `-${readingLine}px 0px -60% 0px`, threshold: 0 }, + { rootMargin: [String(-readingLine) + "px", "0px", "-60%", "0px"].join(" "), threshold: 0 }, ); for (const node of nodes) observer.observe(node); return () => observer.disconnect(); From 066091134b5d30297fb9b9c4460378a689ecb41f Mon Sep 17 00:00:00 2001 From: JUN Date: Sat, 12 Sep 2026 20:13:25 +0900 Subject: [PATCH 9/9] docs(audio): publish browser evidence and CI limitations --- .../031_connections_checks.md | 78 ++++++++++++++++++ .../screenshots/audio-1440-light.png | Bin 0 -> 177047 bytes .../screenshots/audio-320-ko.png | Bin 0 -> 77968 bytes .../screenshots/audio-390-light.png | Bin 0 -> 90755 bytes .../transcription-layer-baseline.png | Bin 0 -> 222807 bytes 5 files changed, 78 insertions(+) create mode 100644 devlog/_plan/260912_audio_apis_stack/031_connections_checks.md create mode 100644 devlog/_plan/260912_audio_apis_stack/screenshots/audio-1440-light.png create mode 100644 devlog/_plan/260912_audio_apis_stack/screenshots/audio-320-ko.png create mode 100644 devlog/_plan/260912_audio_apis_stack/screenshots/audio-390-light.png create mode 100644 devlog/_plan/260912_audio_apis_stack/screenshots/transcription-layer-baseline.png diff --git a/devlog/_plan/260912_audio_apis_stack/031_connections_checks.md b/devlog/_plan/260912_audio_apis_stack/031_connections_checks.md new file mode 100644 index 0000000000..ff74412621 --- /dev/null +++ b/devlog/_plan/260912_audio_apis_stack/031_connections_checks.md @@ -0,0 +1,78 @@ +# Connections audio verification + +## Delivered surface + +Ordinary dependency chain remains open: [#4391](https://github.com/lidge-jun/opencodex/pull/4391) +-> [#4392](https://github.com/lidge-jun/opencodex/pull/4392) +-> [#4395](https://github.com/lidge-jun/opencodex/pull/4395). +No native stack registration, merge, release, deployment or live-service restart. + +The top layer adds validated audio metadata, separate Dictation and Live Voice +blocks, temporary data-key input, cancelable upload/copy, connection-only native +session readiness, client examples and all nine dashboard locales. It does not +add audio models to text completion tests. Invalid/missing metadata leaves existing +key management usable. Configured availability is not account entitlement. + +## Executable proof + +Runtime/UI source head: `f5aefd88af3116bec4f2ddc72c0c6fa974f52a83`. +Remote [run 34690242138](https://github.com/lidge-jun/opencodex/actions/runs/34690242138), +gates job 103544165369: SUCCESS for lint, typecheck, dashboard tests, privacy, +generated skill surface, release syntax, dashboard build and CLI smoke. +Artifact 10296892796 is `dashboard-preview-bb0f30fbab4496b2b69bbc1e8148e59035ba3778`. +Its GUI tree `5ccecf06a8e144e5260f4991047db6fa36f464a1` exactly matches source. +The artifact names the tested PR merge, not a different runtime build. + +Local product tests, typecheck, build, dependency installation and suites: +**NOT RUN**, per owner restriction. Every commit/push used `--no-verify`. +The only local execution was a static Node file server for the CI-built artifact +and browser QA through the already installed Playwright dependency. No proxy +runtime, provider audio request, personal recording or microphone was used. + +## Browser matrix + +Invocation: static artifact server, then `.tmp/audio-browser-run.mjs` driving +`/#integrations/keys` with intercepted synthetic management/audio routes. +Installed agbrowse lacked its documented script command, so the existing +agbrowse Playwright dependency drove a separate CDP browser on port 9231. +No new browser dependency was installed. + +| Scenario | Observed result | +| --- | --- | +| Upload synthetic file | Expected transcript; typed data key only, no management/CSRF headers | +| Copy transcript | Actual browser clipboard contained the exact transcript | +| Live connect/disconnect | session.update, session.started with ID, session.close; no audio frames | +| HTTP 401 | Localized error, raw provider material absent | +| Cancel slow response | No late transcript published | +| Leave/re-enter API tab | Pending resources released, temporary key cleared | +| Storage inspection | No typed key in localStorage/sessionStorage | +| Keyboard | Key input -> file input follows Tab order | +| 1440/1024/768/390/320 | No audio-control overflow; settled screenshots read back | +| Korean 1440/390/320 | Labels fit; no new-section overlap or clipped Korean text | +| Runtime | No page JavaScript errors; no external network requests | + +Initial captures exposed mobile top-bar overlap and a two-line tablet section +strip. Both were fixed; final screenshots below depict the corrected source. +Mobile scroll-spy uses the same 108px reading line as section positioning. + +![Desktop audio controls](screenshots/audio-1440-light.png) +![Mobile audio controls](screenshots/audio-390-light.png) +![Narrow Korean audio controls](screenshots/audio-320-ko.png) + +`screenshots/transcription-layer-baseline.png` separately renders the bottom PR's +own CI artifact (run 34687369123, merge d9771b3133863f5be1b89213029b0a8037dbcb89, +GUI tree 999781a53536c82ccfbfd376083e945bf388c55d). That layer changes GUI test +fixtures and asset provenance only; this capture does not claim the audio UI +exists in the bottom layer. + +## Review and remaining limits + +Inherited security review closed metadata projection and post-readiness failures. +Inherited component review closed endpoint styling, replacement-race and cache +observation coverage. Subsequent xai/grok-4.6 review closed idle status and mobile +scroll-spy alignment. Source verdicts: PASS. Rendered evidence is separate from +source review; neither establishes real provider availability. + +Unrelated journal restore failures remain recorded in `021_streaming_checks.md`. +Old/superseded or cancelled whole runs are not passing final-head evidence. No +whole-suite-green or merge-readiness claim is made. PRs remain open for review. diff --git a/devlog/_plan/260912_audio_apis_stack/screenshots/audio-1440-light.png b/devlog/_plan/260912_audio_apis_stack/screenshots/audio-1440-light.png new file mode 100644 index 0000000000000000000000000000000000000000..81a579bb82bf49107d7e7fca242b152613999ba2 GIT binary patch literal 177047 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%0A2dQ(BJd_6Wy^`FKFG;vr5SWwQG7eQOeJ1B=6 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

jxqNH^yWiBO8BQp!B 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

g3W-Y*LD^&=AxriFRcjM15>smH8&6rRO!-Zh*JP zjBot0N_cykILAzCjz8YQhs9dWDt{cc#V#~}I(Vbzx5Q*Zmd@n8NI%9I3+3Cm(j(

AoR>zm5&)ylkp~=gS4RkS#DpNi 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≫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