@xopcai/xopc 0.0.372 → 0.0.374
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-BQ3PMp2q.js → activity-store-BeYLqqr8.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-D4iAZErG.js → agent-avatar-dicebear-BrA20N2m.js} +1 -1
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BZrOdxou.js +1 -0
- package/dist/gateway/static/root/assets/{agent-plugin-dialog-BYB0G2aF.js → agent-plugin-dialog-B3ux1AhY.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-D0JBkZPV.js → agents-CXlXFU6M.js} +3 -3
- package/dist/gateway/static/root/assets/{agents-admin-api-HBZvVyzF.js → agents-admin-api-B4tgTbWG.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-C1tQxHaP.js → app-management-settings-panel-BXahEVPm.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BfavIBbb.js → appearance-settings-CE2qJMkD.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-CUS5S1qa.js → apps-page-DGVOnCxZ.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-BO3I9zRP.js → archive-plugin-bI9OLnXj.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-iY0KMPSa.js → automations-page-BWL7eT4_.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-LQsa5ohY.js → automations-workspace-BxIShhkZ.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-gFD7vH9R.js → binary-plugins-dOwLzjeH.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-Cft0fd_O.js → block-editor-3n446-mX.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-DxRoKFun.js → browser-automation-inputs-i7h1Svpf.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-C6EWx_Y4.js → browser-automations-page-BmTK7Gtn.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-CKlt_8IS.js → browser-settings-page-DBQ-WETQ.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-CY4SiHcP.js → capabilities-CquUV0DP.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-gjQtMhKu.js → capabilities-D4KFLtqM.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-page-UrQ8EY_o.js → capabilities-page-D-nQ6Zzi.js} +2 -2
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-C9HIr2CZ.js → capabilities-settings-panel-Dx1fpBmH.js} +6 -6
- package/dist/gateway/static/root/assets/{capability-header-actions-uxIiW_2_.js → capability-header-actions-BRMqvQVb.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-By3dmQ4Y.js → channels-DXfEP1FR.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-DeDTSl64.js → chat-terminal-dock-BRdzCjWO.js} +1 -1
- package/dist/gateway/static/root/assets/cloud-credits-BMYMiiNn.js +1 -0
- package/dist/gateway/static/root/assets/{computer-settings-page-Cw1Z1epy.js → computer-settings-page-BhCHMRpD.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-D664WEhj.js → connector-service-page-BXE8Z-Ll.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-sjyjY7Fl.js → connectors-page-D9RZjhw8.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-BVmFrPfX.js → date-picker-BJ7N92wm.js} +1 -1
- package/dist/gateway/static/root/assets/{dependency-picker-YIoP-e5-.js → dependency-picker-gyxCM-hb.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-CzS468vR.js → desktop-pet-B-WM4l-L.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-BPcgZUcK.js → desktop-pet-settings-usJqcew5.js} +1 -1
- package/dist/gateway/static/root/assets/{device-pairing-wizard-Cgba-4m9.js → device-pairing-wizard-DoSR4DGk.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-_nHmZtnY.js → directory-picker-path-field-DqVlV2YH.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-IIky9Iqr.js → extension-debug-page-CbgEonkK.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-C-7ngTTj.js → extension-page-CUule9SU.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-VFWspV08.js → extension-provider-DxlAZ2iy.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-_hovFCWi.js → extension-settings-page-_OFukkEJ.js} +1 -1
- package/dist/gateway/static/root/assets/{fallback-models-CUHrPpGV.js → fallback-models-CiSc3h8i.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-7RKqgU_U.js → gateway-config-swr-BoRkBnsw.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-realtime-BwX_fHUS.js → gateway-realtime-C3WQQY10.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-cLCDEe4D.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-settings-sbaOzlEH.js → gateway-settings-qSUrpS1h.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-jiV5sbEX.js → gateway-startup-retry-TYSQEQcG.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-DjquRIFR.js → home-page-COjDsuzg.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-DO4mXb6I.js → image-generation-api-_pKiNfSq.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-xWWIeT_t.js → imports-page-DdvIj-ZL.js} +1 -1
- package/dist/gateway/static/root/assets/{index-BWDK2eIj.js → index-DEaVabvo.js} +7 -7
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DWazrDgz.js → keyboard-shortcuts-settings-DbqFlNsF.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-CkxDdlYU.js → local-app-workbench-page-REowUzkp.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-BISZ4bi5.js → local-apps-page-DdCMNn_z.js} +1 -1
- package/dist/gateway/static/root/assets/{local-session-drafts-pox8evDV.js → local-session-drafts-Cg9RdsNN.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-COVscAqC.js → locale-store-rWFLl-p4.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-GiCulc9k.js → logs-page-B_6fHDoi.js} +1 -1
- package/dist/gateway/static/root/assets/management-settings-D4vcSRE3.js +1 -0
- package/dist/gateway/static/root/assets/{markdown-view-B_qnAcW2.js → markdown-view-BHFFusQs.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-C0Pc-wSY.js → media-plugins-BVPTMWJI.js} +1 -1
- package/dist/gateway/static/root/assets/messages-C1SUo4gG.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-cache-Jj8dvYcv.js +1 -0
- package/dist/gateway/static/root/assets/{note-detail-panel-CqealmSc.js → note-detail-panel-B7U6EbAi.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-api-sBgBcyLW.js → notes-api-CSLVCZBn.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-BtTU5mnS.js → notes-page-BzHVaH8j.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-capture-button-Yt-BpPRh.js → page-context-capture-button-D7Y4vFRN.js} +1 -1
- package/dist/gateway/static/root/assets/personal-page-E6huhFk4.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-etM6Ijfe.js → product-open-page-CK_czbBx.js} +1 -1
- package/dist/gateway/static/root/assets/{product-preview-content-C6aeRrWe.js → product-preview-content-CU6KlaVP.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-K4ASOTki.js → project-detail-page-c3ye2bdq.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-BssImjwl.js → projects-page-BA1UuuRF.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-Bh9UBb-f.js → registry-api-CoMBYjHD.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-BznC_5H0.js → remote-access-hub-D_FLVZAm.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BJAzqQik.js → runtime-tools-settings-panel-Cxjmwdyf.js} +1 -1
- package/dist/gateway/static/root/assets/{scenes-page-uZUXm03X.js → scenes-page-Av47PHdM.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-SA3Ld7zf.js → schema-form-IM7QeSm3.js} +1 -1
- package/dist/gateway/static/root/assets/{send-session-input-Di3Q846r.js → send-session-input-CLhR1uEp.js} +1 -1
- package/dist/gateway/static/root/assets/{session-api-yjpEz4pP.js → session-api-u_4_kFQt.js} +1 -1
- package/dist/gateway/static/root/assets/{session-manager-CWjhJxJs.js → session-manager-B06ZCie_.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-DdL8zVrF.js → sessions-page-Bol3ScEc.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DpCanqrD.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-CUJQMNF7.js → settings-page-BeVtwwVd.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-BGBWC0Bc.js → setup-status-panel-zTmrMwMm.js} +1 -1
- package/dist/gateway/static/root/assets/{share-link-dialog-HCHM8qnf.js → share-link-dialog-XhiuPKF4.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-DJmyACyp.js → share-preview-page-Vc0TQuZ8.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-api-c0dfWz6h.js → shares-api-BVSb1vh4.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-BzqcGh7W.js → shares-settings-Bkhkt_6M.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-api-9XSn7R8L.js → skill-api-Dgwc707w.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-Ge5XymHa.js → skill-reload-api-BsLx_FXd.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-DwyDfh9g.js → skills-page-CFIte-vM.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-B6axBPLO.js → system-settings-panel-Ck6zGDHb.js} +1 -1
- package/dist/gateway/static/root/assets/{task-detail-page-ftCTqBDB.js → task-detail-page-C3ZPEZqw.js} +1 -1
- package/dist/gateway/static/root/assets/{task-expandable-content-hYcgCEE3.js → task-expandable-content-DxeBTJvh.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-BOfCKgl6.js → theme-store-KcQhCg1-.js} +1 -1
- package/dist/gateway/static/root/assets/{tracing-settings-page-DDWFXcST.js → tracing-settings-page-DJRAP3nb.js} +1 -1
- package/dist/gateway/static/root/assets/{tunnel-api-BYU2v0im.js → tunnel-api-CjxMpAm2.js} +1 -1
- package/dist/gateway/static/root/assets/usage-page-Dnhp9xQ4.js +1 -0
- package/dist/gateway/static/root/assets/{use-autosave-CSckcjFx.js → use-autosave-C-IWxbzm.js} +1 -1
- package/dist/gateway/static/root/assets/{use-composer-attachments-OFlldkKg.js → use-composer-attachments-Dxphaxk-.js} +1 -1
- package/dist/gateway/static/root/assets/{use-file-preview-expanded-BF1TeNXP.js → use-file-preview-expanded-Du37Gofn.js} +2 -2
- package/dist/gateway/static/root/assets/{user-model-page-CqnGITYf.js → user-model-page-sx1lNWKf.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-GEw5x2fU.js → voice-api-key-field-7BImy8Jc.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-3yyVwXln.js → voice-config-api-6MINytQl.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-BDM7BEKH.js → work-discovery-overlay-CVd_y3K2.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BBwx-tUb.js → workflow-run-setup-panel-Dwg40uGK.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-BHigZn6E.js → workflows-page-BQR4JdXB.js} +1 -1
- package/dist/gateway/static/root/assets/{workspace-api-CTm8s6Jr.js → workspace-api-CXiyhTwv.js} +2 -2
- package/dist/gateway/static/root/assets/xopc-cloud-account-card-CaKc_J7j.js +1 -0
- package/dist/gateway/static/root/index.html +26 -26
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/cloud-credits.js +80 -0
- package/dist/packages/gateway-contract/src/index.js +2 -0
- package/dist/packages/realtime-protocol/src/voice.js +11 -0
- package/dist/src/agent/client-error-format.d.ts +1 -1
- package/dist/src/agent/client-error-format.js +7 -0
- package/dist/src/agent/embedded/run-turn.js +39 -3
- package/dist/src/agent/embedded/xopc-stream-bridge.js +18 -0
- package/dist/src/agent/image/generation/openai-images-provider.d.ts +2 -1
- package/dist/src/agent/image/generation/openai-images-provider.js +13 -7
- package/dist/src/agent/image/generation/providers/xopc-cloud.js +14 -2
- package/dist/src/agent-config/schema.d.ts +13 -0
- package/dist/src/agent-config/schema.js +2 -0
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/shared.js +2 -1
- package/dist/src/cli/commands/models.js +41 -0
- package/dist/src/computer/runtime.js +3 -0
- package/dist/src/config/gateway-bind.js +2 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/models-json.d.ts +8 -8
- package/dist/src/config/schema.d.ts +13 -12
- package/dist/src/config/schema.js +7 -3
- package/dist/src/daemon/constants.js +14 -9
- package/dist/src/daemon/index.js +4 -1
- package/dist/src/daemon/install-plan.js +2 -1
- package/dist/src/daemon/launchd.js +2 -1
- package/dist/src/daemon/schtasks.js +2 -1
- package/dist/src/daemon/service-audit.js +2 -1
- package/dist/src/daemon/service.js +2 -1
- package/dist/src/daemon/systemd.js +2 -1
- package/dist/src/gateway/client-ip.js +2 -1
- package/dist/src/gateway/hono/app.js +2 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -0
- package/dist/src/gateway/hono/middleware/auth.d.ts +3 -5
- package/dist/src/gateway/hono/middleware/auth.js +56 -14
- package/dist/src/gateway/hono/middleware/logger.js +2 -1
- package/dist/src/gateway/hono/middleware/public-device-rate-limit.d.ts +16 -0
- package/dist/src/gateway/hono/middleware/public-device-rate-limit.js +43 -0
- package/dist/src/gateway/hono/middleware/strict-rate-limit.js +3 -2
- package/dist/src/gateway/hono/oauth-async.js +2 -1
- package/dist/src/gateway/hono/routes/browser.js +2 -1
- package/dist/src/gateway/hono/routes/config.js +2 -1
- package/dist/src/gateway/hono/routes/device-pairing-approval.d.ts +3 -8
- package/dist/src/gateway/hono/routes/device-pairing-approval.js +5 -20
- package/dist/src/gateway/hono/routes/devices.js +25 -9
- package/dist/src/gateway/hono/routes/endpoint-tools.js +1 -0
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/personal-agent.js +3 -2
- package/dist/src/gateway/hono/routes/public-gateway.js +6 -6
- package/dist/src/gateway/hono/routes/shares.js +2 -1
- package/dist/src/gateway/hono/routes/usage.js +15 -5
- package/dist/src/gateway/hono/routes/voice.js +1 -0
- package/dist/src/gateway/host.js +9 -4
- package/dist/src/gateway/index.js +8 -4
- package/dist/src/gateway/listen.js +2 -1
- package/dist/src/gateway/rate-limit/auth-policy.js +7 -11
- package/dist/src/gateway/rate-limit/buckets.d.ts +6 -0
- package/dist/src/gateway/rate-limit/buckets.js +211 -189
- package/dist/src/gateway/rate-limit/env-flags.js +4 -1
- package/dist/src/gateway/rate-limit/index.js +13 -4
- package/dist/src/gateway/runtime-config.js +5 -3
- package/dist/src/gateway/security/audit.js +3 -2
- package/dist/src/gateway/security/browser-session.js +2 -1
- package/dist/src/gateway/security/device-request-proof.d.ts +28 -0
- package/dist/src/gateway/security/device-request-proof.js +122 -0
- package/dist/src/gateway/security/loopback.js +7 -6
- package/dist/src/gateway/service.js +8 -6
- package/dist/src/gateway/tailscale-auth.js +2 -1
- package/dist/src/gateway/ws-security.js +2 -1
- package/dist/src/infra/rate-limit/failure-limiter.js +78 -90
- package/dist/src/infra/rate-limit/index.js +11 -3
- package/dist/src/infra/rate-limit/keyed-store.js +52 -47
- package/dist/src/infra/rate-limit/rate-limiter.js +55 -58
- package/dist/src/infra/update-service-refresh.js +2 -1
- package/dist/src/personal-agent/repository.d.ts +3 -0
- package/dist/src/personal-agent/repository.js +1 -0
- package/dist/src/personal-agent/service.d.ts +6 -2
- package/dist/src/personal-agent/service.js +4 -3
- package/dist/src/platform/client.d.ts +2 -2
- package/dist/src/platform/contracts.d.ts +2 -2
- package/dist/src/providers/model-call.js +15 -1
- package/dist/src/providers/xopc-cloud-account-service.d.ts +7 -23
- package/dist/src/providers/xopc-cloud-account-service.js +49 -29
- package/dist/src/providers/xopc-cloud-credit-budget.d.ts +14 -0
- package/dist/src/providers/xopc-cloud-credit-budget.js +61 -0
- package/dist/src/realtime/runtime.js +6 -0
- package/dist/src/realtime/tickets.d.ts +1 -0
- package/dist/src/realtime/tickets.js +4 -0
- package/dist/src/remote-access/exposure-guards.js +2 -1
- package/dist/src/share/share-rate-limit.js +2 -1
- package/dist/src/share/share-url.js +2 -1
- package/dist/src/share/site-share-router.js +2 -1
- package/dist/src/storage/sqlite/ai-usage-repository.d.ts +9 -0
- package/dist/src/storage/sqlite/ai-usage-repository.js +94 -5
- package/dist/src/storage/sqlite/device-access-repository.d.ts +7 -0
- package/dist/src/storage/sqlite/device-access-repository.js +22 -4
- package/dist/src/storage/sqlite/migrations/242_cloud_usage_receipts.sql +5 -0
- package/dist/src/storage/sqlite/migrations/243_voice_opening_greetings.sql +12 -0
- package/dist/src/storage/sqlite/migrations/244_device_request_proofs.sql +8 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tunnel/broker-client.js +2 -0
- package/dist/src/tunnel/tunnel-config.js +2 -2
- package/dist/src/tunnel/tunnel-rate-limit.js +2 -1
- package/dist/src/usage/cloud-capability.d.ts +2 -0
- package/dist/src/usage/cloud-capability.js +34 -0
- package/dist/src/usage/cloud-receipts.d.ts +10 -0
- package/dist/src/usage/cloud-receipts.js +55 -0
- package/dist/src/usage/cost.js +1 -0
- package/dist/src/usage/recorder.js +3 -1
- package/dist/src/usage/scenario-registry.js +15 -0
- package/dist/src/utils/logger/redact.js +4 -1
- package/dist/src/voice/platform-streams.d.ts +6 -0
- package/dist/src/voice/platform-streams.js +43 -4
- package/dist/src/voice/realtime/agentEngine.js +138 -83
- package/dist/src/voice/realtime/engine.d.ts +6 -0
- package/dist/src/voice/realtime/opening-greeting.d.ts +12 -0
- package/dist/src/voice/realtime/opening-greeting.js +22 -0
- package/dist/src/voice/realtime/runtime.js +116 -22
- package/dist/src/voice/realtime/runtime.types.d.ts +4 -0
- package/dist/src/voice/stt/providers/xopc-cloud-transcription.js +28 -14
- package/dist/src/voice/tts/providers/xopc-cloud-speech.js +39 -29
- package/dist/src/voice/tts/speech-provider-types.d.ts +1 -0
- package/package.json +5 -5
- package/dist/gateway/static/root/assets/agent-defaults-settings-panel-BdxzGSiO.js +0 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-BheKaO6d.js +0 -1
- package/dist/gateway/static/root/assets/management-settings-GUpOcTqa.js +0 -1
- package/dist/gateway/static/root/assets/messages-Bb2GK-Nj.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-cache-DyPPksMP.js +0 -1
- package/dist/gateway/static/root/assets/personal-page-C8SEQpEu.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DmawLOHR.js +0 -1
- package/dist/gateway/static/root/assets/usage-page-BvPcpxTQ.js +0 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { getSqliteDatabase, init_transaction, runSqliteWriteTransaction } from "./transaction.js";
|
|
2
|
+
import { creditMicroSchema } from "../../../packages/gateway-contract/src/cloud-credits.js";
|
|
3
|
+
import { init_src } from "../../../packages/gateway-contract/src/index.js";
|
|
2
4
|
//#region src/storage/sqlite/ai-usage-repository.ts
|
|
5
|
+
init_src();
|
|
3
6
|
init_transaction();
|
|
4
7
|
const ERROR_SUMMARY_LIMIT = 512;
|
|
5
8
|
const AI_USAGE_RETENTION_MS = 4320 * 60 * 60 * 1e3;
|
|
@@ -43,6 +46,41 @@ function pruneAiUsageEvents(cutoffMs) {
|
|
|
43
46
|
function getAiUsageEvent(id) {
|
|
44
47
|
return getSqliteDatabase().prepare("SELECT * FROM ai_usage_events WHERE id = ?").get(id);
|
|
45
48
|
}
|
|
49
|
+
function setAiUsageCloudRequest(id, requestId) {
|
|
50
|
+
if (!/^[A-Za-z0-9_.:-]{1,128}$/.test(requestId)) return;
|
|
51
|
+
runSqliteWriteTransaction((db) => {
|
|
52
|
+
db.prepare(`UPDATE ai_usage_events SET cloud_request_id = ?, updated_at = ?
|
|
53
|
+
WHERE id = ? AND provider = 'xopc-cloud' AND cloud_billing_json IS NULL`).run(requestId, Date.now(), id);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function setAiUsageCloudReceipt(id, receipt) {
|
|
57
|
+
if (![
|
|
58
|
+
"settled",
|
|
59
|
+
"released",
|
|
60
|
+
"expired",
|
|
61
|
+
"reserved",
|
|
62
|
+
"pending"
|
|
63
|
+
].includes(receipt.state)) return;
|
|
64
|
+
const state = receipt.state === "settled" ? "settled" : ["released", "expired"].includes(receipt.state) ? "released" : "pending";
|
|
65
|
+
const charged = state === "settled" ? receipt.chargedCreditMicro : state === "released" ? "0" : null;
|
|
66
|
+
if (state === "settled" && charged === null) return;
|
|
67
|
+
creditMicroSchema.parse(receipt.reservedCreditMicro);
|
|
68
|
+
creditMicroSchema.parse(receipt.releasedCreditMicro);
|
|
69
|
+
if (charged !== null) creditMicroSchema.parse(charged);
|
|
70
|
+
const billing = {
|
|
71
|
+
source: "xopc_cloud",
|
|
72
|
+
state,
|
|
73
|
+
requestId: receipt.requestId,
|
|
74
|
+
reservedCreditMicro: state === "pending" ? receipt.reservedCreditMicro : "0",
|
|
75
|
+
chargedCreditMicro: charged,
|
|
76
|
+
releasedCreditMicro: receipt.releasedCreditMicro
|
|
77
|
+
};
|
|
78
|
+
runSqliteWriteTransaction((db) => {
|
|
79
|
+
db.prepare(`UPDATE ai_usage_events SET cloud_billing_json = ?, updated_at = ?
|
|
80
|
+
WHERE id = ? AND provider = 'xopc-cloud' AND cloud_request_id = ?
|
|
81
|
+
AND COALESCE(json_extract(cloud_billing_json, '$.state'), '') NOT IN ('settled', 'released')`).run(JSON.stringify(billing), Date.now(), id, receipt.requestId);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
46
84
|
function queryWhere(query) {
|
|
47
85
|
const clauses = ["started_at >= ?", "started_at < ?"];
|
|
48
86
|
const values = [query.from, query.to];
|
|
@@ -64,6 +102,18 @@ function queryWhere(query) {
|
|
|
64
102
|
values
|
|
65
103
|
};
|
|
66
104
|
}
|
|
105
|
+
function pendingAiUsageCloudReceiptIds(query) {
|
|
106
|
+
const { sql, values } = queryWhere(query);
|
|
107
|
+
return getSqliteDatabase().prepare(`SELECT id FROM ai_usage_events WHERE ${sql}
|
|
108
|
+
AND provider = 'xopc-cloud' AND cloud_request_id IS NOT NULL
|
|
109
|
+
AND COALESCE(json_extract(cloud_billing_json, '$.state'), '') NOT IN ('settled', 'released')
|
|
110
|
+
ORDER BY updated_at ASC, id ASC LIMIT 4`).all(...values).map((row) => row.id);
|
|
111
|
+
}
|
|
112
|
+
function markAiUsageCloudReceiptAttempt(id) {
|
|
113
|
+
runSqliteWriteTransaction((db) => {
|
|
114
|
+
db.prepare(`UPDATE ai_usage_events SET updated_at = ? WHERE id = ? AND provider = 'xopc-cloud'`).run(Date.now(), id);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
67
117
|
function usd(microusd) {
|
|
68
118
|
return (microusd / 1e6).toFixed(6).replace(/0+$/, "").replace(/\.$/, "") || "0";
|
|
69
119
|
}
|
|
@@ -81,19 +131,44 @@ function totalsFromRow(row) {
|
|
|
81
131
|
cacheWriteTokens: Number(row.cache_write_tokens ?? 0),
|
|
82
132
|
totalTokens: Number(row.total_tokens ?? 0),
|
|
83
133
|
knownCostUsd: usd(Number(row.known_cost_microusd ?? 0)),
|
|
84
|
-
costCompleteness:
|
|
134
|
+
costCompleteness: Number(row.external_calls ?? 0) === 0 || unknownCostCalls === Number(row.external_calls) ? "unknown" : unknownCostCalls > 0 ? "partial" : "complete",
|
|
135
|
+
billing: {
|
|
136
|
+
external: {
|
|
137
|
+
calls: Number(row.external_calls ?? 0),
|
|
138
|
+
estimatedCostUsd: usd(Number(row.known_cost_microusd ?? 0)),
|
|
139
|
+
unknownCostCalls
|
|
140
|
+
},
|
|
141
|
+
localCalls: Number(row.local_calls ?? 0),
|
|
142
|
+
cloud: {
|
|
143
|
+
calls: Number(row.cloud_calls ?? 0),
|
|
144
|
+
settledCalls: Number(row.cloud_final_calls ?? 0),
|
|
145
|
+
pendingCalls: Number(row.cloud_pending_calls ?? 0),
|
|
146
|
+
unavailableCalls: Number(row.cloud_unavailable_calls ?? 0),
|
|
147
|
+
chargedCreditMicro: Number(row.cloud_final_calls ?? 0) > 0 ? String(row.cloud_charged_micro ?? "0") : null,
|
|
148
|
+
reservedCreditMicro: Number(row.cloud_known_reservations ?? 0) > 0 ? String(row.cloud_reserved_micro ?? "0") : null
|
|
149
|
+
}
|
|
150
|
+
}
|
|
85
151
|
};
|
|
86
152
|
}
|
|
87
153
|
const TOTALS_SQL = `COUNT(*) AS calls,
|
|
88
154
|
SUM(CASE WHEN status = 'succeeded' THEN 1 ELSE 0 END) AS succeeded_calls,
|
|
89
155
|
SUM(CASE WHEN status IN ('failed', 'aborted', 'unknown') THEN 1 ELSE 0 END) AS failed_calls,
|
|
90
|
-
SUM(CASE WHEN cost_source <> 'local' AND estimated_cost_microusd IS NULL THEN 1 ELSE 0 END) AS unknown_cost_calls,
|
|
156
|
+
SUM(CASE WHEN provider <> 'xopc-cloud' AND cost_source <> 'local' AND estimated_cost_microusd IS NULL THEN 1 ELSE 0 END) AS unknown_cost_calls,
|
|
157
|
+
SUM(CASE WHEN provider <> 'xopc-cloud' AND cost_source <> 'local' THEN 1 ELSE 0 END) AS external_calls,
|
|
158
|
+
SUM(CASE WHEN provider <> 'xopc-cloud' AND cost_source = 'local' THEN 1 ELSE 0 END) AS local_calls,
|
|
159
|
+
SUM(CASE WHEN provider = 'xopc-cloud' THEN 1 ELSE 0 END) AS cloud_calls,
|
|
160
|
+
SUM(CASE WHEN provider = 'xopc-cloud' AND json_extract(cloud_billing_json, '$.state') IN ('settled', 'released') THEN 1 ELSE 0 END) AS cloud_final_calls,
|
|
161
|
+
SUM(CASE WHEN provider = 'xopc-cloud' AND json_extract(cloud_billing_json, '$.state') = 'pending' THEN 1 ELSE 0 END) AS cloud_pending_calls,
|
|
162
|
+
SUM(CASE WHEN provider = 'xopc-cloud' AND cloud_billing_json IS NULL THEN 1 ELSE 0 END) AS cloud_unavailable_calls,
|
|
163
|
+
SUM(CASE WHEN provider = 'xopc-cloud' AND cloud_billing_json IS NOT NULL THEN 1 ELSE 0 END) AS cloud_known_reservations,
|
|
164
|
+
CAST(COALESCE(SUM(CASE WHEN provider = 'xopc-cloud' THEN CAST(json_extract(cloud_billing_json, '$.chargedCreditMicro') AS INTEGER) ELSE 0 END), 0) AS TEXT) AS cloud_charged_micro,
|
|
165
|
+
CAST(COALESCE(SUM(CASE WHEN provider = 'xopc-cloud' THEN CAST(json_extract(cloud_billing_json, '$.reservedCreditMicro') AS INTEGER) ELSE 0 END), 0) AS TEXT) AS cloud_reserved_micro,
|
|
91
166
|
COALESCE(SUM(input_tokens), 0) AS input_tokens,
|
|
92
167
|
COALESCE(SUM(output_tokens), 0) AS output_tokens,
|
|
93
168
|
COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens,
|
|
94
169
|
COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens,
|
|
95
170
|
COALESCE(SUM(total_tokens), 0) AS total_tokens,
|
|
96
|
-
COALESCE(SUM(estimated_cost_microusd), 0) AS known_cost_microusd`;
|
|
171
|
+
COALESCE(SUM(CASE WHEN provider <> 'xopc-cloud' AND cost_source <> 'local' THEN estimated_cost_microusd ELSE 0 END), 0) AS known_cost_microusd`;
|
|
97
172
|
/** Aggregate all retained AI usage records for the requested conversations. */
|
|
98
173
|
function summarizeAiUsageByConversations(conversationIds) {
|
|
99
174
|
const uniqueIds = [...new Set(conversationIds.filter(Boolean))];
|
|
@@ -140,6 +215,19 @@ function summarizeAiUsage(query) {
|
|
|
140
215
|
}
|
|
141
216
|
function eventFromRow(row) {
|
|
142
217
|
const pricingSnapshot = typeof row.pricing_snapshot_json === "string" ? JSON.parse(row.pricing_snapshot_json) : void 0;
|
|
218
|
+
const estimatedCostUsd = row.provider === "xopc-cloud" || row.cost_source === "local" || row.estimated_cost_microusd === null ? void 0 : usd(Number(row.estimated_cost_microusd));
|
|
219
|
+
const billing = row.provider === "xopc-cloud" ? row.cloud_billing_json ? JSON.parse(String(row.cloud_billing_json)) : {
|
|
220
|
+
source: "xopc_cloud",
|
|
221
|
+
state: "unavailable",
|
|
222
|
+
requestId: row.cloud_request_id ?? void 0,
|
|
223
|
+
reservedCreditMicro: null,
|
|
224
|
+
chargedCreditMicro: null,
|
|
225
|
+
releasedCreditMicro: null
|
|
226
|
+
} : row.cost_source === "local" ? { source: "local" } : {
|
|
227
|
+
source: "external_provider",
|
|
228
|
+
currency: "USD",
|
|
229
|
+
estimatedCostUsd
|
|
230
|
+
};
|
|
143
231
|
return {
|
|
144
232
|
id: String(row.id),
|
|
145
233
|
traceId: String(row.trace_id),
|
|
@@ -165,7 +253,8 @@ function eventFromRow(row) {
|
|
|
165
253
|
cacheWriteTokens: row.cache_write_tokens === null ? void 0 : Number(row.cache_write_tokens),
|
|
166
254
|
reasoningTokens: row.reasoning_tokens === null ? void 0 : Number(row.reasoning_tokens),
|
|
167
255
|
totalTokens: row.total_tokens === null ? void 0 : Number(row.total_tokens),
|
|
168
|
-
estimatedCostUsd
|
|
256
|
+
estimatedCostUsd,
|
|
257
|
+
billing,
|
|
169
258
|
costSource: String(row.cost_source),
|
|
170
259
|
pricingSnapshot,
|
|
171
260
|
errorSummary: row.error_summary ? String(row.error_summary) : void 0
|
|
@@ -198,4 +287,4 @@ function listAiUsageTrace(traceId) {
|
|
|
198
287
|
ORDER BY started_at ASC, id ASC`).all(traceId).map(eventFromRow);
|
|
199
288
|
}
|
|
200
289
|
//#endregion
|
|
201
|
-
export { AI_USAGE_RETENTION_MS, finishAiUsageEvent, getAiUsageEvent, getAiUsageEventView, insertAiUsageEvent, listAiUsageEvents, listAiUsageTrace, markStaleAiUsageEventsUnknown, pruneAiUsageEvents, summarizeAiUsage, summarizeAiUsageByConversations };
|
|
290
|
+
export { AI_USAGE_RETENTION_MS, finishAiUsageEvent, getAiUsageEvent, getAiUsageEventView, insertAiUsageEvent, listAiUsageEvents, listAiUsageTrace, markAiUsageCloudReceiptAttempt, markStaleAiUsageEventsUnknown, pendingAiUsageCloudReceiptIds, pruneAiUsageEvents, setAiUsageCloudReceipt, setAiUsageCloudRequest, summarizeAiUsage, summarizeAiUsageByConversations };
|
|
@@ -8,6 +8,7 @@ export type DeviceRecord = {
|
|
|
8
8
|
platform: DevicePlatform;
|
|
9
9
|
extensionId?: string;
|
|
10
10
|
publicKeyJwk: DevicePublicKeyJwk;
|
|
11
|
+
requestProofVersion: number;
|
|
11
12
|
scopes: GatewayScope[];
|
|
12
13
|
createdAt: number;
|
|
13
14
|
lastSeenAt?: number;
|
|
@@ -45,7 +46,12 @@ export declare function buildRefreshProofMessage(input: {
|
|
|
45
46
|
nonce: string;
|
|
46
47
|
requestId: string;
|
|
47
48
|
nextRefreshToken: string;
|
|
49
|
+
requestProofVersion?: number;
|
|
48
50
|
}): string;
|
|
51
|
+
export declare class DeviceRefreshRateLimitError extends Error {
|
|
52
|
+
readonly retryAfterMs: number;
|
|
53
|
+
constructor(retryAfterMs: number);
|
|
54
|
+
}
|
|
49
55
|
export declare function rotateDeviceRefreshToken(input: {
|
|
50
56
|
refreshToken: string;
|
|
51
57
|
timestamp: number;
|
|
@@ -53,6 +59,7 @@ export declare function rotateDeviceRefreshToken(input: {
|
|
|
53
59
|
requestId: string;
|
|
54
60
|
nextRefreshToken: string;
|
|
55
61
|
signature: string;
|
|
62
|
+
requestProofVersion?: number;
|
|
56
63
|
now?: number;
|
|
57
64
|
}): DeviceTokenPair;
|
|
58
65
|
export declare function revokeDevice(deviceId: string, now?: number): boolean;
|
|
@@ -2,6 +2,8 @@ import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
|
2
2
|
import { getSqliteDatabase, init_transaction, runSqliteWriteTransaction } from "./transaction.js";
|
|
3
3
|
import { initialDeviceRefreshTokenSchema } from "../../../packages/gateway-contract/src/device-pairing.js";
|
|
4
4
|
import { init_src } from "../../../packages/gateway-contract/src/index.js";
|
|
5
|
+
import { buckets } from "../../gateway/rate-limit/buckets.js";
|
|
6
|
+
import { init_rate_limit } from "../../gateway/rate-limit/index.js";
|
|
5
7
|
import { init_gateway_scopes, parseGatewayScopes } from "../../gateway/security/gateway-scopes.js";
|
|
6
8
|
import crypto from "node:crypto";
|
|
7
9
|
//#region src/storage/sqlite/device-access-repository.ts
|
|
@@ -23,6 +25,7 @@ function deviceFromRow(row) {
|
|
|
23
25
|
displayName: row.display_name,
|
|
24
26
|
platform: row.platform,
|
|
25
27
|
publicKeyJwk: JSON.parse(row.public_key_jwk),
|
|
28
|
+
requestProofVersion: row.request_proof_version,
|
|
26
29
|
...row.extension_id ? { extensionId: row.extension_id } : {},
|
|
27
30
|
scopes: parseGatewayScopes(row.scopes_json),
|
|
28
31
|
createdAt: row.created_at,
|
|
@@ -109,7 +112,7 @@ function authenticateDeviceAccessToken(token, now = Date.now()) {
|
|
|
109
112
|
};
|
|
110
113
|
}
|
|
111
114
|
function buildRefreshProofMessage(input) {
|
|
112
|
-
return `xopc-device-refresh-v2\n${input.credentialId}\n${input.timestamp}\n${input.nonce}\n${input.requestId}\n${input.nextRefreshToken}`;
|
|
115
|
+
return `xopc-device-refresh-v2\n${input.credentialId}\n${input.timestamp}\n${input.nonce}\n${input.requestId}\n${input.nextRefreshToken}${input.requestProofVersion ? `\nrequest-proof:${input.requestProofVersion}` : ""}`;
|
|
113
116
|
}
|
|
114
117
|
function verifyRefreshProof(publicKeyJwk, message, signature) {
|
|
115
118
|
try {
|
|
@@ -146,9 +149,17 @@ function rotateDeviceRefreshToken(input) {
|
|
|
146
149
|
timestamp: input.timestamp,
|
|
147
150
|
nonce: input.nonce,
|
|
148
151
|
requestId: input.requestId,
|
|
149
|
-
nextRefreshToken: input.nextRefreshToken
|
|
152
|
+
nextRefreshToken: input.nextRefreshToken,
|
|
153
|
+
requestProofVersion: input.requestProofVersion
|
|
150
154
|
});
|
|
151
155
|
if (!verifyRefreshProof(JSON.parse(deviceRow.public_key_jwk), message, input.signature)) throw new Error("Invalid refresh proof");
|
|
156
|
+
const refreshBudget = buckets.deviceRefresh().consume(credential.device_id);
|
|
157
|
+
if (!refreshBudget.allowed) throw new DeviceRefreshRateLimitError(refreshBudget.retryAfterMs);
|
|
158
|
+
if (deviceRow.request_proof_version > (input.requestProofVersion ?? 0)) throw new Error("Request proof downgrade denied");
|
|
159
|
+
if (input.requestProofVersion === 1 && deviceRow.request_proof_version === 0) {
|
|
160
|
+
db.prepare("UPDATE devices SET request_proof_version = 1 WHERE device_id = ?").run(credential.device_id);
|
|
161
|
+
db.prepare("UPDATE device_access_sessions SET revoked_at = ? WHERE device_id = ? AND revoked_at IS NULL").run(now, credential.device_id);
|
|
162
|
+
}
|
|
152
163
|
if (credential.replaced_by === nextParsed.id && credential.rotation_request_id === input.requestId) {
|
|
153
164
|
const nextCredential = db.prepare(`
|
|
154
165
|
SELECT credential_id, device_id, token_hash, expires_at, replaced_by,
|
|
@@ -198,9 +209,10 @@ function revokeDevice(deviceId, now = Date.now()) {
|
|
|
198
209
|
return changed;
|
|
199
210
|
});
|
|
200
211
|
}
|
|
201
|
-
var ACCESS_TOKEN_TTL_MS, REFRESH_TOKEN_TTL_MS, REFRESH_PROOF_MAX_AGE_MS, ACCESS_TOKEN_PREFIX, REFRESH_TOKEN_PREFIX;
|
|
212
|
+
var ACCESS_TOKEN_TTL_MS, REFRESH_TOKEN_TTL_MS, REFRESH_PROOF_MAX_AGE_MS, ACCESS_TOKEN_PREFIX, REFRESH_TOKEN_PREFIX, DeviceRefreshRateLimitError;
|
|
202
213
|
var init_device_access_repository = __esmMin((() => {
|
|
203
214
|
init_src();
|
|
215
|
+
init_rate_limit();
|
|
204
216
|
init_gateway_scopes();
|
|
205
217
|
init_transaction();
|
|
206
218
|
ACCESS_TOKEN_TTL_MS = 900 * 1e3;
|
|
@@ -208,7 +220,13 @@ var init_device_access_repository = __esmMin((() => {
|
|
|
208
220
|
REFRESH_PROOF_MAX_AGE_MS = 300 * 1e3;
|
|
209
221
|
ACCESS_TOKEN_PREFIX = "xopc_at_";
|
|
210
222
|
REFRESH_TOKEN_PREFIX = "xopc_rt_";
|
|
223
|
+
DeviceRefreshRateLimitError = class extends Error {
|
|
224
|
+
constructor(retryAfterMs) {
|
|
225
|
+
super("Too many device refresh requests");
|
|
226
|
+
this.retryAfterMs = retryAfterMs;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
211
229
|
}));
|
|
212
230
|
//#endregion
|
|
213
231
|
init_device_access_repository();
|
|
214
|
-
export { authenticateDeviceAccessToken, buildRefreshProofMessage, createDevice, getDevice, init_device_access_repository, issueDeviceTokenPair, listDevices, registerInitialDeviceRefreshToken, revokeDevice, rotateDeviceRefreshToken };
|
|
232
|
+
export { DeviceRefreshRateLimitError, authenticateDeviceAccessToken, buildRefreshProofMessage, createDevice, getDevice, init_device_access_repository, issueDeviceTokenPair, listDevices, registerInitialDeviceRefreshToken, revokeDevice, rotateDeviceRefreshToken };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
ALTER TABLE ai_usage_events ADD COLUMN cloud_request_id TEXT;
|
|
2
|
+
ALTER TABLE ai_usage_events ADD COLUMN cloud_billing_json TEXT
|
|
3
|
+
CHECK(cloud_billing_json IS NULL OR json_valid(cloud_billing_json));
|
|
4
|
+
CREATE UNIQUE INDEX idx_ai_usage_cloud_request
|
|
5
|
+
ON ai_usage_events(cloud_request_id) WHERE cloud_request_id IS NOT NULL;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
CREATE TABLE voice_opening_greetings (
|
|
2
|
+
principal_id TEXT NOT NULL,
|
|
3
|
+
conversation_id TEXT NOT NULL,
|
|
4
|
+
logical_call_id TEXT NOT NULL,
|
|
5
|
+
response_id TEXT NOT NULL,
|
|
6
|
+
state TEXT NOT NULL CHECK(state IN ('claimed', 'completed', 'skipped', 'interrupted', 'failed', 'expired')),
|
|
7
|
+
reason TEXT,
|
|
8
|
+
created_at INTEGER NOT NULL,
|
|
9
|
+
updated_at INTEGER NOT NULL,
|
|
10
|
+
PRIMARY KEY (principal_id, conversation_id, logical_call_id)
|
|
11
|
+
);
|
|
12
|
+
CREATE INDEX idx_voice_opening_greetings_created ON voice_opening_greetings(created_at);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
ALTER TABLE devices ADD COLUMN request_proof_version INTEGER NOT NULL DEFAULT 0;
|
|
2
|
+
CREATE TABLE device_request_nonces (
|
|
3
|
+
device_id TEXT NOT NULL REFERENCES devices(device_id) ON DELETE CASCADE,
|
|
4
|
+
nonce TEXT NOT NULL,
|
|
5
|
+
expires_at INTEGER NOT NULL,
|
|
6
|
+
PRIMARY KEY (device_id, nonce)
|
|
7
|
+
) WITHOUT ROWID;
|
|
8
|
+
CREATE INDEX device_request_nonces_expiry ON device_request_nonces(expires_at);
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 165;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 244;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -57,7 +57,7 @@ function applySingleMigration(db, migration, discardOldScenes = false) {
|
|
|
57
57
|
*/
|
|
58
58
|
function applyPendingMigrations(db, options = {}) {
|
|
59
59
|
db.function("xopc_operation_id", () => currentOperationId() ?? null);
|
|
60
|
-
const targetVersion = options.targetVersion ??
|
|
60
|
+
const targetVersion = options.targetVersion ?? 244;
|
|
61
61
|
let currentVersion = readSchemaVersion(db);
|
|
62
62
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
63
63
|
if (!options.migrationsDir && currentVersion < 165) throw new DatabaseSchemaTooOldError(currentVersion, 165);
|
|
@@ -95,7 +95,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
95
95
|
}
|
|
96
96
|
/** Inspect schema version without mutating the database. */
|
|
97
97
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
98
|
-
const appVersion = options.targetVersion ??
|
|
98
|
+
const appVersion = options.targetVersion ?? 244;
|
|
99
99
|
const dbVersion = readSchemaVersion(db);
|
|
100
100
|
if (dbVersion > appVersion) return {
|
|
101
101
|
dbVersion,
|
|
@@ -137,7 +137,7 @@ var init_runner = __esmMin((() => {
|
|
|
137
137
|
init_errors();
|
|
138
138
|
log = createLogger("Sqlite:Migrations");
|
|
139
139
|
XOPC_DB_BASELINE_SCHEMA_VERSION = 165;
|
|
140
|
-
XOPC_DB_SCHEMA_VERSION =
|
|
140
|
+
XOPC_DB_SCHEMA_VERSION = 244;
|
|
141
141
|
}));
|
|
142
142
|
//#endregion
|
|
143
143
|
init_runner();
|
|
@@ -29,6 +29,8 @@ const registrationResponseSchema = z.object({
|
|
|
29
29
|
var TunnelBrokerClient = class {
|
|
30
30
|
constructor(baseUrl) {
|
|
31
31
|
this.baseUrl = baseUrl;
|
|
32
|
+
const url = new URL(baseUrl);
|
|
33
|
+
if (url.protocol !== "https:" || url.username || url.password) throw new Error("Tunnel broker requires HTTPS without URL credentials");
|
|
32
34
|
}
|
|
33
35
|
apiUrl(path) {
|
|
34
36
|
return `${this.baseUrl.replace(/\/+$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TunnelConfigSchema, TunnelConsentSchema, init_schema } from "../config/schema.js";
|
|
1
|
+
import { TunnelBrokerUrlSchema, TunnelConfigSchema, TunnelConsentSchema, init_schema } from "../config/schema.js";
|
|
2
2
|
import { getTunnelRegistrationSecretMeta, isMaskedTunnelSecretPatchValue } from "./env.js";
|
|
3
3
|
import { assertTunnelAutoStartAllowed, buildTunnelConsentRecord, hasValidTunnelConsent } from "./consent.js";
|
|
4
4
|
import { z } from "zod";
|
|
@@ -6,7 +6,7 @@ import { z } from "zod";
|
|
|
6
6
|
init_schema();
|
|
7
7
|
const TunnelConfigPatchSchema = z.object({
|
|
8
8
|
enabled: z.boolean().optional(),
|
|
9
|
-
brokerUrl:
|
|
9
|
+
brokerUrl: TunnelBrokerUrlSchema.optional(),
|
|
10
10
|
registrationSecret: z.union([z.string(), z.null()]).optional(),
|
|
11
11
|
autoStart: z.boolean().optional(),
|
|
12
12
|
subdomain: z.string().optional(),
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
3
|
import { buckets } from "../gateway/rate-limit/buckets.js";
|
|
4
|
-
import "../gateway/rate-limit/index.js";
|
|
4
|
+
import { init_rate_limit } from "../gateway/rate-limit/index.js";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
//#region src/tunnel/tunnel-rate-limit.ts
|
|
7
|
+
init_rate_limit();
|
|
7
8
|
init_logger();
|
|
8
9
|
const log = createLogger("TunnelRateLimit");
|
|
9
10
|
function tokenKey(token) {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { startAiUsageCall } from "./recorder.js";
|
|
2
|
+
import { getEmbeddedExecutionRunId, getEmbeddedExecutionSession } from "../agent/embedded/execution-context.js";
|
|
3
|
+
//#region src/usage/cloud-capability.ts
|
|
4
|
+
/** Cloud tools receive independent receipts even when the parent chat uses BYOK. */
|
|
5
|
+
async function trackCloudCapabilityUsage(model, operation, action) {
|
|
6
|
+
const call = startAiUsageCall({
|
|
7
|
+
provider: "xopc-cloud",
|
|
8
|
+
id: model,
|
|
9
|
+
baseUrl: void 0,
|
|
10
|
+
cost: {
|
|
11
|
+
input: 0,
|
|
12
|
+
output: 0,
|
|
13
|
+
cacheRead: 0,
|
|
14
|
+
cacheWrite: 0
|
|
15
|
+
}
|
|
16
|
+
}, {
|
|
17
|
+
operation,
|
|
18
|
+
conversationId: getEmbeddedExecutionSession(),
|
|
19
|
+
runId: getEmbeddedExecutionRunId()
|
|
20
|
+
});
|
|
21
|
+
try {
|
|
22
|
+
const result = await call.run(action);
|
|
23
|
+
call.finish({ status: "succeeded" });
|
|
24
|
+
return result;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
call.finish({
|
|
27
|
+
status: "failed",
|
|
28
|
+
errorSummary: error instanceof Error ? error.message : String(error)
|
|
29
|
+
});
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { trackCloudCapabilityUsage };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SimpleStreamOptions } from '@earendil-works/pi-ai';
|
|
2
|
+
export declare function withUsageReceiptContext<T>(id: string, action: () => T): T;
|
|
3
|
+
export declare function captureCloudUsageResponse(response: {
|
|
4
|
+
status: number;
|
|
5
|
+
headers: Headers | Record<string, string>;
|
|
6
|
+
}): void;
|
|
7
|
+
/** Preserve existing response hooks while linking the physical Cloud request to its usage row. */
|
|
8
|
+
export declare function withCloudUsageResponse(options?: SimpleStreamOptions): SimpleStreamOptions;
|
|
9
|
+
/** Bounded, read-only synchronization. Missing authorization or receipt never becomes a zero charge. */
|
|
10
|
+
export declare function reconcileCloudUsageReceipt(id: string): Promise<void>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { init_connection, isXopcDatabaseOpen } from "../storage/sqlite/connection.js";
|
|
2
|
+
import { getAiUsageEvent, markAiUsageCloudReceiptAttempt, setAiUsageCloudReceipt, setAiUsageCloudRequest } from "../storage/sqlite/ai-usage-repository.js";
|
|
3
|
+
import { XopcCloudAccountService } from "../providers/xopc-cloud-account-service.js";
|
|
4
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
+
//#region src/usage/cloud-receipts.ts
|
|
6
|
+
init_connection();
|
|
7
|
+
const calls = new AsyncLocalStorage();
|
|
8
|
+
const active = /* @__PURE__ */ new Map();
|
|
9
|
+
function withUsageReceiptContext(id, action) {
|
|
10
|
+
return calls.run({ id }, action);
|
|
11
|
+
}
|
|
12
|
+
function captureCloudUsageResponse(response) {
|
|
13
|
+
if (response.status < 200 || response.status >= 300) return;
|
|
14
|
+
const id = calls.getStore()?.id;
|
|
15
|
+
const requestId = new Headers(response.headers).get("x-xopc-request-id");
|
|
16
|
+
if (id && requestId && isXopcDatabaseOpen()) try {
|
|
17
|
+
setAiUsageCloudRequest(id, requestId);
|
|
18
|
+
} catch {}
|
|
19
|
+
}
|
|
20
|
+
/** Preserve existing response hooks while linking the physical Cloud request to its usage row. */
|
|
21
|
+
function withCloudUsageResponse(options) {
|
|
22
|
+
return {
|
|
23
|
+
...options,
|
|
24
|
+
onResponse: async (response, model) => {
|
|
25
|
+
if (model.provider === "xopc-cloud") captureCloudUsageResponse(response);
|
|
26
|
+
await options?.onResponse?.(response, model);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Bounded, read-only synchronization. Missing authorization or receipt never becomes a zero charge. */
|
|
31
|
+
function reconcileCloudUsageReceipt(id) {
|
|
32
|
+
const running = active.get(id);
|
|
33
|
+
if (running) return running;
|
|
34
|
+
if (!isXopcDatabaseOpen() || active.size >= 4) return Promise.resolve();
|
|
35
|
+
const row = getAiUsageEvent(id);
|
|
36
|
+
if (row?.provider !== "xopc-cloud" || typeof row.cloud_request_id !== "string") return Promise.resolve();
|
|
37
|
+
const state = row.cloud_billing_json ? JSON.parse(String(row.cloud_billing_json)).state : void 0;
|
|
38
|
+
if (state === "settled" || state === "released") return Promise.resolve();
|
|
39
|
+
const requestId = row.cloud_request_id;
|
|
40
|
+
markAiUsageCloudReceiptAttempt(id);
|
|
41
|
+
const service = new XopcCloudAccountService({ fetchImpl: (url, init) => fetch(url, {
|
|
42
|
+
...init,
|
|
43
|
+
signal: AbortSignal.timeout(5e3)
|
|
44
|
+
}) });
|
|
45
|
+
const sync = (async () => {
|
|
46
|
+
try {
|
|
47
|
+
const receipt = await service.getReceipt(requestId);
|
|
48
|
+
if (receipt?.requestId === requestId && isXopcDatabaseOpen()) setAiUsageCloudReceipt(id, receipt);
|
|
49
|
+
} catch {}
|
|
50
|
+
})().finally(() => active.delete(id));
|
|
51
|
+
active.set(id, sync);
|
|
52
|
+
return sync;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { captureCloudUsageResponse, reconcileCloudUsageReceipt, withCloudUsageResponse, withUsageReceiptContext };
|
package/dist/src/usage/cost.js
CHANGED
|
@@ -17,6 +17,7 @@ function pricingSnapshot(model) {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
function classifyCostSource(model) {
|
|
20
|
+
if (model.provider === "xopc-cloud") return "unknown";
|
|
20
21
|
if (isLocalBaseUrl(model.baseUrl)) return "local";
|
|
21
22
|
return Object.values(pricingSnapshot(model)).some((price) => price > 0) ? "model_catalog" : "unknown";
|
|
22
23
|
}
|
|
@@ -5,6 +5,7 @@ import { startObservation } from "../observability/runtime.js";
|
|
|
5
5
|
import { finishAiUsageEvent, insertAiUsageEvent } from "../storage/sqlite/ai-usage-repository.js";
|
|
6
6
|
import { classifyCostSource, dollarsToMicrousd, pricingSnapshot } from "./cost.js";
|
|
7
7
|
import { resolveAiUsageScenario } from "./scenario-registry.js";
|
|
8
|
+
import { reconcileCloudUsageReceipt, withUsageReceiptContext } from "./cloud-receipts.js";
|
|
8
9
|
import { randomUUID } from "node:crypto";
|
|
9
10
|
//#region src/usage/recorder.ts
|
|
10
11
|
init_connection();
|
|
@@ -67,7 +68,7 @@ function startAiUsageCall(model, context, input) {
|
|
|
67
68
|
let completed = false;
|
|
68
69
|
return {
|
|
69
70
|
id,
|
|
70
|
-
run: observation.run,
|
|
71
|
+
run: (fn) => observation.run(() => withUsageReceiptContext(id, fn)),
|
|
71
72
|
finish(result) {
|
|
72
73
|
if (completed) return;
|
|
73
74
|
completed = true;
|
|
@@ -90,6 +91,7 @@ function startAiUsageCall(model, context, input) {
|
|
|
90
91
|
...result,
|
|
91
92
|
estimatedCostMicrousd: costSource === "local" ? 0 : costSource === "unknown" ? void 0 : dollarsToMicrousd(result.usage?.cost?.total)
|
|
92
93
|
});
|
|
94
|
+
if (model.provider === "xopc-cloud") reconcileCloudUsageReceipt(id);
|
|
93
95
|
} catch (err) {
|
|
94
96
|
log.error({
|
|
95
97
|
err,
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
//#region src/usage/scenario-registry.ts
|
|
2
2
|
const SCENARIOS = {
|
|
3
|
+
"images.generate": {
|
|
4
|
+
category: "other",
|
|
5
|
+
reasonKey: "usage.reason.imageGeneration",
|
|
6
|
+
trigger: "agent"
|
|
7
|
+
},
|
|
8
|
+
"audio.transcribe": {
|
|
9
|
+
category: "other",
|
|
10
|
+
reasonKey: "usage.reason.audioTranscription",
|
|
11
|
+
trigger: "user"
|
|
12
|
+
},
|
|
13
|
+
"audio.speech": {
|
|
14
|
+
category: "other",
|
|
15
|
+
reasonKey: "usage.reason.audioSpeech",
|
|
16
|
+
trigger: "agent"
|
|
17
|
+
},
|
|
3
18
|
"agent.answer": {
|
|
4
19
|
category: "chat",
|
|
5
20
|
reasonKey: "usage.reason.agentAnswer",
|
|
@@ -62,8 +62,9 @@ var init_redact = __esmMin((() => {
|
|
|
62
62
|
KEEP_PREFIX_LENGTH = 6;
|
|
63
63
|
KEEP_SUFFIX_LENGTH = 4;
|
|
64
64
|
DEFAULT_REDACT_PATTERNS = [
|
|
65
|
+
/X-Xopc-Proof\s*[:=]\s*([A-Za-z0-9._-]+)/gi,
|
|
65
66
|
/\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD)\b\s*[=:]\s*((?:\\?["'])?)([^\s"'\\]+)\1/gi,
|
|
66
|
-
/"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken|privateKey)"\s*:\s*"([^"]+)"/gi,
|
|
67
|
+
/"(?:apiKey|token|secret|password|passwd|accessToken|refreshToken|privateKey|signature|x-xopc-proof)"\s*:\s*"([^"]+)"/gi,
|
|
67
68
|
/(?:__Host-xopc-session|xopc-local-session)=([A-Za-z0-9_-]{43})/g,
|
|
68
69
|
/Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)/gi,
|
|
69
70
|
/\b(sk-[A-Za-z0-9_-]{8,})\b/gi,
|
|
@@ -88,6 +89,8 @@ var init_redact = __esmMin((() => {
|
|
|
88
89
|
"cookie",
|
|
89
90
|
"set-cookie",
|
|
90
91
|
"signedpayload",
|
|
92
|
+
"signature",
|
|
93
|
+
"x-xopc-proof",
|
|
91
94
|
"credential",
|
|
92
95
|
"credentials"
|
|
93
96
|
]);
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { StreamingSttOpenRequest, StreamingSttSession } from '../media-understanding/types.js';
|
|
2
2
|
import type { SpeechSynthesisStreamResult } from './tts/speech-provider-types.js';
|
|
3
|
+
export declare class PlatformVoiceError extends Error {
|
|
4
|
+
readonly status?: number;
|
|
5
|
+
readonly code?: string;
|
|
6
|
+
constructor(message: string, status?: number, code?: string);
|
|
7
|
+
get clientMessage(): string;
|
|
8
|
+
}
|
|
3
9
|
export declare function openPlatformStt(request: StreamingSttOpenRequest): Promise<StreamingSttSession>;
|
|
4
10
|
export declare function openPlatformTts(request: {
|
|
5
11
|
baseUrl: string;
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import WebSocket from "ws";
|
|
2
2
|
//#region src/voice/platform-streams.ts
|
|
3
3
|
const MAX_BUFFERED_BYTES = 512 * 1024;
|
|
4
|
+
const MAX_ERROR_BODY_BYTES = 16 * 1024;
|
|
5
|
+
var PlatformVoiceError = class extends Error {
|
|
6
|
+
constructor(message, status, code) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.name = "PlatformVoiceError";
|
|
11
|
+
}
|
|
12
|
+
get clientMessage() {
|
|
13
|
+
if (this.code === "realtime_quota_workspace_budget") return "XOPC Cloud voice workspace budget is exhausted. Increase the workspace budget or choose another voice provider.";
|
|
14
|
+
if (this.status === 402 || this.code === "realtime_quota_user_credits") return "XOPC Cloud voice has insufficient Credit or quota. Check your Cloud balance and workspace budget, or choose another voice provider.";
|
|
15
|
+
if (this.status === 401 || this.status === 403) return "XOPC Cloud voice authorization failed. Sign in again and check your access to this voice model.";
|
|
16
|
+
if (this.status === 429 || this.code?.startsWith("realtime_quota_")) return "XOPC Cloud voice quota or rate limit reached. Try again later or choose another voice provider.";
|
|
17
|
+
return "XOPC Cloud voice is unavailable. Try again later or choose another voice provider.";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
4
20
|
function connect(url, token, signal, onEvent, onFailure) {
|
|
5
21
|
signal.throwIfAborted();
|
|
6
22
|
const socket = new WebSocket(url, {
|
|
@@ -30,15 +46,38 @@ function connect(url, token, signal, onEvent, onFailure) {
|
|
|
30
46
|
if (binary) throw new Error("Unexpected platform binary event");
|
|
31
47
|
const event = JSON.parse(raw.toString());
|
|
32
48
|
if (!event || typeof event.type !== "string") throw new Error("Invalid platform voice event");
|
|
33
|
-
if (event.type === "error") throw new
|
|
49
|
+
if (event.type === "error") throw new PlatformVoiceError(event.error?.message ?? "Platform voice failed", void 0, event.error?.code);
|
|
34
50
|
onEvent(event);
|
|
35
51
|
} catch (error) {
|
|
36
52
|
fail(error instanceof Error ? error : /* @__PURE__ */ new Error("Invalid platform voice event"));
|
|
37
53
|
}
|
|
38
54
|
});
|
|
39
55
|
socket.on("unexpected-response", (_request, response) => {
|
|
40
|
-
response.
|
|
41
|
-
|
|
56
|
+
const fallback = `Platform voice rejected connection (${response.statusCode})`;
|
|
57
|
+
let bytes = 0;
|
|
58
|
+
const chunks = [];
|
|
59
|
+
const reject = () => {
|
|
60
|
+
let detail;
|
|
61
|
+
try {
|
|
62
|
+
detail = JSON.parse(Buffer.concat(chunks).toString("utf8"))?.error;
|
|
63
|
+
} catch {}
|
|
64
|
+
const code = typeof detail?.code === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(detail.code) ? detail.code : void 0;
|
|
65
|
+
const message = typeof detail?.message === "string" ? detail.message.slice(0, 1e3) : void 0;
|
|
66
|
+
fail(new PlatformVoiceError(`${fallback}${code ? `: ${code}` : ""}${message ? `: ${message}` : ""}`, response.statusCode, code));
|
|
67
|
+
};
|
|
68
|
+
response.on("data", (chunk) => {
|
|
69
|
+
bytes += chunk.length;
|
|
70
|
+
if (bytes > MAX_ERROR_BODY_BYTES) {
|
|
71
|
+
chunks.length = 0;
|
|
72
|
+
reject();
|
|
73
|
+
response.destroy();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
chunks.push(chunk);
|
|
77
|
+
});
|
|
78
|
+
response.once("end", reject);
|
|
79
|
+
response.once("error", reject);
|
|
80
|
+
response.once("aborted", reject);
|
|
42
81
|
});
|
|
43
82
|
socket.on("error", fail);
|
|
44
83
|
socket.on("close", () => fail(/* @__PURE__ */ new Error("Platform voice disconnected before completion")));
|
|
@@ -265,4 +304,4 @@ async function openPlatformTts(request) {
|
|
|
265
304
|
};
|
|
266
305
|
}
|
|
267
306
|
//#endregion
|
|
268
|
-
export { openPlatformStt, openPlatformTts };
|
|
307
|
+
export { PlatformVoiceError, openPlatformStt, openPlatformTts };
|