@xopcai/xopc 0.0.144 → 0.0.145
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/feishu/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/src/adapters/streaming-adapter.js +1 -1
- package/dist/extensions/telegram/src/inbound-processor.js +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/api/api.js +1 -1
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js +1 -1
- package/dist/gateway/static/root/assets/Combination-DolwnuKi.js +41 -0
- package/dist/gateway/static/root/assets/{activity-center-store-jITiX2vJ.js → activity-center-store-3l9ahUqb.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-EAVnrCAs.js → agent-avatar-dicebear-BEUxdhZx.js} +1 -1
- package/dist/gateway/static/root/assets/agents-1bpT0xRO.js +2 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-CRHSZJWi.js +1 -0
- package/dist/gateway/static/root/assets/{appearance-settings-DSkm9bp_.js → appearance-settings-BunADYJb.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-Cc4JgVUQ.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-C99N0vV6.js → archive-plugin-B6vTkH2A.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CHTDqME5.js → attachment-load-BjZXzaNv.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-CBt6U3-S.js → automation-suggestion-card-ZDA-Stg1.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-mb0fdVpq.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-BRtxOLlM.js → binary-plugins-DYZfLvPz.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CUlXfgXd.js → block-editor-C3MUlYC9.js} +46 -46
- package/dist/gateway/static/root/assets/browser-settings-page-Bb0xhaMN.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-BV76yN9a.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CUcGKZqC.js +2 -0
- package/dist/gateway/static/root/assets/channel-recipient-api-fTlTnWB4.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BlSDMGdy.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-CJauF1CW.js +1 -0
- package/dist/gateway/static/root/assets/{code-editor--tMO38Tr.js → code-editor-D4_k-6QN.js} +1 -1
- package/dist/gateway/static/root/assets/connector-logo-BVFNT2zW.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-CMgAFGvb.js +2 -0
- package/dist/gateway/static/root/assets/copy-to-clipboard-HOJ79TWv.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet-C-Drhqyk.js → desktop-pet-_jUmoXd3.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-CRGiolqF.js → desktop-pet-settings-DwGOy4AQ.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-Dw1L3M84.js → directory-picker-path-field-akvF0F-u.js} +1 -1
- package/dist/gateway/static/root/assets/{dist-SwiI0Xu3.js → dist-C99RGfAH.js} +1 -1
- package/dist/gateway/static/root/assets/dreaming-settings-BLetQiYY.js +3 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-Dcl8LJG_.js → extension-debug-page-Cof2ncxE.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-i_BXmO_x.js → extension-page-CwWktRNz.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-CuggWOIl.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-api-B7sxIbza.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-DIv0eKMf.js +1 -0
- package/dist/gateway/static/root/assets/gateway-startup-retry-DtoLihQE.js +1 -0
- package/dist/gateway/static/root/assets/goal-create-dialog-C4Kw0mfS.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-BMmzYpZL.js +4 -0
- package/dist/gateway/static/root/assets/goals-config-api-B0jm5JaA.js +1 -0
- package/dist/gateway/static/root/assets/goals-page-CGeT77PC.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-N5_iL7es.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-XzH3lbRR.js +1 -0
- package/dist/gateway/static/root/assets/{html-workspace-editor-COqmtESs.js → html-workspace-editor-C3DKAkMc.js} +1 -1
- package/dist/gateway/static/root/assets/index-CSLF84-Q.css +1 -0
- package/dist/gateway/static/root/assets/index-I-ZCenVw.js +259 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-x_v0nrIi.js +1 -0
- package/dist/gateway/static/root/assets/locale-store-CiM-D9jA.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-BSh1t8cD.js +7 -0
- package/dist/gateway/static/root/assets/markdown-editor-BvUebh3C.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-BTRFTK0w.js → markdown-split-5rJx5flj.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-BABfIZPU.js → media-plugins-DYXccwAd.js} +1 -1
- package/dist/gateway/static/root/assets/messages-Cb1VMeqP.js +5 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BNpTenAI.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-88QBHW9a.js → notes-page-BgLyndCO.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-teklkhcE.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-je95DZOf.js → page-tabs-6Al9_sGz.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BJbwi7Rc.js → preview-open-alternatives-Cm3G72Hv.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CCB_dKoh.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-VOAFAHYj.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-USYu64Ru.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-qWMQ9mod.js → save-bar-controls-BYtfzu_F.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-kxd270Gs.js → schedule-field-CvIRc6yK.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-DLnD-MuD.js → schema-form-CQuKx0n7.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-oGC8O5h_.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-D4rP66R1.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-DQprvu5J.js → settings-collapsible-section-CF3HHM-t.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-Cqq7Y98y.js → settings-form-section-CwWjoRN6.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-b6rXULL5.js → settings-loading-skeleton-aUFmJa13.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-BtBVz3cX.js → settings-page-B2UUU9TF.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-BNJbtklV.js → settings-page-layout-DqpbTWqI.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Ck9keROI.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-BOhlPsS1.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-BYLsCuqj.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BNHUyVML.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-CpXbQH4n.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-Cq9J7i7v.js → system-settings-panel-CWxSvuwD.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-Bt6vAkSB.js → theme-store-C9Dxcppm.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-CdSwi1gT.js → typed-models-lib-qROAvqTw.js} +1 -1
- package/dist/gateway/static/root/assets/use-channel-catalog-DE9cpn4G.js +1 -0
- package/dist/gateway/static/root/assets/user-context-page-DbNEK4Yp.js +1 -0
- package/dist/gateway/static/root/assets/user-profile-fields-editor-CWxc0ghP.js +1 -0
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-B8hlamVB.js → user-profile-settings-panel-DW-Up6vt.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-Dx01V8qF.js → utils-D53uRXVr.js} +1 -1
- package/dist/gateway/static/root/assets/{vendor-codemirror-C1WKjCk5.js → vendor-codemirror--gTq3YAI.js} +17 -17
- package/dist/gateway/static/root/assets/voice-api-key-field-Cs7MOrg0.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-D1ziG1Pp.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-D_x9ELJp.js +3 -0
- package/dist/gateway/static/root/assets/workflows-page-DHGV_n6i.js +11 -0
- package/dist/gateway/static/root/index.html +10 -10
- package/dist/package.js +1 -1
- package/dist/src/activity/emitter.js +1 -1
- package/dist/src/agent/agent-manager.js +4 -4
- package/dist/src/agent/code-intelligence/binary.js +1 -1
- package/dist/src/agent/context/project-context.js +2 -2
- package/dist/src/agent/context/workspace-seed.js +2 -2
- package/dist/src/agent/embedded/run-for-session.js +1 -1
- package/dist/src/agent/embedded/session-runner.js +3 -3
- package/dist/src/agent/embedded/session-runner.js.map +1 -1
- package/dist/src/agent/embedded/xopc-auth-storage.d.ts +3 -1
- package/dist/src/agent/embedded/xopc-auth-storage.js +11 -1
- package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -1
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +1 -1
- package/dist/src/agent/memory/dreaming/config.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +1 -1
- package/dist/src/agent/service.js +3 -3
- package/dist/src/agent/session/session-inspector.js +2 -2
- package/dist/src/agent/skills/hub-pull.js +1 -1
- package/dist/src/agent/tools/composio-tool.js +1 -1
- package/dist/src/agent/tools/goal-tool.js +2 -2
- package/dist/src/agent/tools/workflow-tool.js +1 -1
- package/dist/src/agent/tools/xopc-use-tool.js +1 -1
- package/dist/src/agent/workspace-runtime/registry.js +2 -2
- package/dist/src/agent-runtime/index.js +1 -1
- package/dist/src/agent-runtime/runtime-profile.js +1 -1
- package/dist/src/automations/api/routes.js +1 -1
- package/dist/src/automations/service/action-executor.js +2 -2
- package/dist/src/browser/cdp-local-launcher.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +3 -3
- package/dist/src/browser/providers/cloakbrowser.js +2 -2
- package/dist/src/browser/providers/playwright-doctor.js +1 -1
- package/dist/src/browser/stealth.js +1 -1
- package/dist/src/capabilities/store-connector.js +1 -1
- package/dist/src/chat-commands/agent-edit.js +1 -1
- package/dist/src/chat-commands/builtins/config.js +1 -1
- package/dist/src/chat-commands/builtins/goal.js +2 -2
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/cli/commands/agent.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +2 -2
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/goal.js +2 -2
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/project.js +3 -3
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.js +3 -3
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/schema.d.ts +60 -5
- package/dist/src/config/schema.js +30 -9
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/connectors/composio-catalog.js +11 -2
- package/dist/src/connectors/composio-catalog.js.map +1 -1
- package/dist/src/connectors/composio-sessions.js +1 -1
- package/dist/src/connectors/composio.js +1 -1
- package/dist/src/connectors/connector-memory-sync.js +135 -26
- package/dist/src/connectors/connector-memory-sync.js.map +1 -1
- package/dist/src/daemon/launchd.js +1 -1
- package/dist/src/daemon/systemd.js +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -1
- package/dist/src/extensions/loader.js +2 -2
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +11 -0
- package/dist/src/gateway/hono/lib/config-payload.js +1 -0
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/lib/static-ui.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +6 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
- package/dist/src/gateway/hono/routes/home.js +2 -2
- package/dist/src/gateway/hono/routes/memory.js +13 -3
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/projects.js +5 -5
- package/dist/src/gateway/hono/routes/status.js +3 -0
- package/dist/src/gateway/hono/routes/status.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.d.ts +5 -5
- package/dist/src/gateway/hono/routes/voice.js +288 -62
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +1 -1
- package/dist/src/gateway/hono/routes/you.js +11 -7
- package/dist/src/gateway/hono/routes/you.js.map +1 -1
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/ports.js +1 -1
- package/dist/src/gateway/service.d.ts +8 -0
- package/dist/src/gateway/service.js +47 -17
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/goals/goal-workflow-judge.js +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/ports.js +1 -1
- package/dist/src/infra/ssh-tunnel.js +1 -1
- package/dist/src/infra/update-check.js +1 -1
- package/dist/src/knowledge/connected-knowledge-pipeline.d.ts +21 -0
- package/dist/src/knowledge/connected-knowledge-pipeline.js +267 -0
- package/dist/src/knowledge/connected-knowledge-pipeline.js.map +1 -0
- package/dist/src/knowledge/coordinator.d.ts +10 -0
- package/dist/src/knowledge/coordinator.js +52 -0
- package/dist/src/knowledge/coordinator.js.map +1 -0
- package/dist/src/knowledge/index.d.ts +2 -0
- package/dist/src/knowledge/index.js +3 -1
- package/dist/src/knowledge/ingestion-service.d.ts +7 -1
- package/dist/src/knowledge/ingestion-service.js +28 -0
- package/dist/src/knowledge/ingestion-service.js.map +1 -1
- package/dist/src/knowledge/types.d.ts +18 -0
- package/dist/src/media-understanding/audio-transcription-runner.js +5 -3
- package/dist/src/media-understanding/audio-transcription-runner.js.map +1 -1
- package/dist/src/media-understanding/runner.js +8 -3
- package/dist/src/media-understanding/runner.js.map +1 -1
- package/dist/src/media-understanding/types.d.ts +7 -0
- package/dist/src/migrations/runner.js +1 -1
- package/dist/src/notes/service.js +3 -3
- package/dist/src/notes/store.js +1 -1
- package/dist/src/projects/project-service.js +1 -1
- package/dist/src/projects/workspace.js +1 -1
- package/dist/src/routing/agent-session-key.js +1 -1
- package/dist/src/routing/resolve-route.js +1 -1
- package/dist/src/routing/resolve-tui-session-key.js +1 -1
- package/dist/src/session/init-session-turn.js +1 -1
- package/dist/src/session/resolve-session.js +1 -1
- package/dist/src/session/session-title.js +2 -2
- package/dist/src/session/store.js +1 -1
- package/dist/src/storage/sqlite/connection.js +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -1
- package/dist/src/storage/sqlite/index.js +2 -2
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +26 -1
- package/dist/src/storage/sqlite/knowledge-repository.js +120 -6
- package/dist/src/storage/sqlite/knowledge-repository.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/037_connected_knowledge_pipeline.sql +47 -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/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/row-mappers.js +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/backends/embedded-backend.js +4 -4
- package/dist/src/tui/clipboard-image.js +1 -1
- package/dist/src/tui/tui-command-formatters.js +2 -2
- package/dist/src/tui/tui-commands.js +3 -3
- package/dist/src/tui/tui.js +1 -1
- package/dist/src/user-context/projection.d.ts +13 -4
- package/dist/src/user-context/projection.js +19 -7
- package/dist/src/user-context/projection.js.map +1 -1
- package/dist/src/utils/index.js +1 -1
- package/dist/src/utils/logger/index.js +1 -1
- package/dist/src/utils/logger.js +1 -1
- package/dist/src/voice/language-profile.d.ts +20 -0
- package/dist/src/voice/language-profile.js +156 -0
- package/dist/src/voice/language-profile.js.map +1 -0
- package/dist/src/voice/local/model-files.d.ts +14 -0
- package/dist/src/voice/local/model-files.js +94 -0
- package/dist/src/voice/local/model-files.js.map +1 -0
- package/dist/src/voice/local/model-manager.d.ts +18 -0
- package/dist/src/voice/local/model-manager.js +135 -0
- package/dist/src/voice/local/model-manager.js.map +1 -0
- package/dist/src/voice/local/models.d.ts +28 -0
- package/dist/src/voice/local/models.js +101 -0
- package/dist/src/voice/local/models.js.map +1 -0
- package/dist/src/voice/local/runtime-client.d.ts +26 -0
- package/dist/src/voice/local/runtime-client.js +166 -0
- package/dist/src/voice/local/runtime-client.js.map +1 -0
- package/dist/src/voice/local/runtime-dispatcher.d.ts +9 -0
- package/dist/src/voice/local/runtime-dispatcher.js +22 -0
- package/dist/src/voice/local/runtime-dispatcher.js.map +1 -0
- package/dist/src/voice/local/runtime-worker.d.ts +1 -0
- package/dist/src/voice/local/runtime-worker.js +249 -0
- package/dist/src/voice/local/runtime-worker.js.map +1 -0
- package/dist/src/voice/local/wav.d.ts +6 -0
- package/dist/src/voice/local/wav.js +69 -0
- package/dist/src/voice/local/wav.js.map +1 -0
- package/dist/src/voice/metadata/builtin.js +46 -0
- package/dist/src/voice/metadata/builtin.js.map +1 -1
- package/dist/src/voice/stt/factory.js +8 -2
- package/dist/src/voice/stt/factory.js.map +1 -1
- package/dist/src/voice/stt/providers/alibaba-transcription.js +14 -8
- package/dist/src/voice/stt/providers/alibaba-transcription.js.map +1 -1
- package/dist/src/voice/stt/providers/index.d.ts +1 -0
- package/dist/src/voice/stt/providers/index.js +2 -1
- package/dist/src/voice/stt/providers/local-transcription.d.ts +2 -0
- package/dist/src/voice/stt/providers/local-transcription.js +51 -0
- package/dist/src/voice/stt/providers/local-transcription.js.map +1 -0
- package/dist/src/voice/stt/providers/openai-transcription.js +5 -3
- package/dist/src/voice/stt/providers/openai-transcription.js.map +1 -1
- package/dist/src/voice/stt/transcribe-core.js +5 -4
- package/dist/src/voice/stt/transcribe-core.js.map +1 -1
- package/dist/src/voice/stt/types.d.ts +4 -0
- package/dist/src/voice/stt/types.js +5 -4
- package/dist/src/voice/stt/types.js.map +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/dist/src/voice/tts/providers/edge-speech.js.map +1 -1
- package/dist/src/voice/tts/types.js +3 -3
- package/dist/src/voice/tts/types.js.map +1 -1
- package/dist/src/work-items/work-item-service.js +1 -1
- package/dist/src/workflows/draft/workflow-draft-service.js +1 -1
- package/dist/src/workflows/service/workflow-session-bridge.js +3 -3
- package/package.json +3 -1
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
- package/dist/gateway/static/root/assets/agents-5rTuJOtT.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-Bu568NpC.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-DWetalKp.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-ICUfxeOz.js +0 -4
- package/dist/gateway/static/root/assets/browser-settings-page-D7Bj4MKi.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-Bg1ntFSD.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-C-8I-Ktn.js +0 -2
- package/dist/gateway/static/root/assets/channel-recipient-api-DuulnhIj.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-Dm5EqQyK.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-B5lVrUsN.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-BOzjh-OK.js +0 -2
- package/dist/gateway/static/root/assets/dreaming-settings-CF21cbjo.js +0 -3
- package/dist/gateway/static/root/assets/extension-settings-page-CWIi7xsn.js +0 -1
- package/dist/gateway/static/root/assets/fetch-8QNN25Sh.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-2sLP2kkA.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-DMHiZc-2.js +0 -1
- package/dist/gateway/static/root/assets/gateway-startup-retry-5YBnS9Fj.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-CAODVDiA.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-_d4DjmeX.js +0 -4
- package/dist/gateway/static/root/assets/goals-config-api-B4qeLKQS.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-lgsop664.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-Dx_1ZNgb.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-DeTseEk_.js +0 -1
- package/dist/gateway/static/root/assets/index-CphxClbs.css +0 -1
- package/dist/gateway/static/root/assets/index-VWvztcst.js +0 -257
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-BpwpMmlk.js +0 -1
- package/dist/gateway/static/root/assets/locale-store-Ba72bFmP.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-9_hbTg3j.js +0 -7
- package/dist/gateway/static/root/assets/markdown-editor-jD1LPiP-.js +0 -2
- package/dist/gateway/static/root/assets/messages-CzKxM6I4.js +0 -5
- package/dist/gateway/static/root/assets/models-hub-panel-B1CUvZxh.js +0 -2
- package/dist/gateway/static/root/assets/notes-workbench-CerZOeLW.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-BmrApldl.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-tTn-k76N.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-DeA2-pOi.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-D4kvvcL1.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-CI1OG4gq.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-cmclUJzS.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page--zO-A1W1.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-DNJvqA67.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DWr_0Xgn.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-DEGsTbJx.js +0 -2
- package/dist/gateway/static/root/assets/use-channel-catalog-COXIfXcl.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-C-V73ZaJ.js +0 -1
- package/dist/gateway/static/root/assets/user-profile-fields-editor-B1cxoGkh.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-DGpIUelZ.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-fzWTxF26.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-Dt_x8LGg.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-Bg02zEUh.js +0 -11
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { DEFAULT_LOCAL_VOICE_MODEL_ID, isLocalVoiceModelInstalled } from "./local/models.js";
|
|
4
|
+
import { startLocalVoiceModelInstall } from "./local/model-manager.js";
|
|
5
|
+
//#region src/voice/language-profile.ts
|
|
6
|
+
init_logger();
|
|
7
|
+
const log = createLogger("Voice:Language");
|
|
8
|
+
function resolveVoiceLanguageProfile(language) {
|
|
9
|
+
return language === "zh" ? {
|
|
10
|
+
language,
|
|
11
|
+
sttLanguage: "auto",
|
|
12
|
+
edgeLanguage: "zh-CN",
|
|
13
|
+
edgeVoice: "zh-CN-XiaoxiaoNeural"
|
|
14
|
+
} : {
|
|
15
|
+
language,
|
|
16
|
+
sttLanguage: "en",
|
|
17
|
+
edgeLanguage: "en-US",
|
|
18
|
+
edgeVoice: "en-US-MichelleNeural"
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function inferProductLanguageFromEnvironment() {
|
|
22
|
+
return [
|
|
23
|
+
process.env.LC_ALL,
|
|
24
|
+
process.env.LC_MESSAGES,
|
|
25
|
+
process.env.LANG,
|
|
26
|
+
Intl.DateTimeFormat().resolvedOptions().locale
|
|
27
|
+
].find((value) => typeof value === "string" && value.trim())?.trim().toLowerCase().startsWith("zh") ? "zh" : "en";
|
|
28
|
+
}
|
|
29
|
+
function hasLegacyManualLanguageSelection(config) {
|
|
30
|
+
const local = config.tools?.media?.audio?.providers?.["xopc-local"];
|
|
31
|
+
const edge = config.messages?.tts?.providers?.edge;
|
|
32
|
+
return typeof local?.language === "string" && local.language.trim().length > 0 || typeof edge?.voice === "string" && edge.voice.trim().length > 0 || typeof edge?.lang === "string" && edge.lang.trim().length > 0;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Install safe product defaults while preserving every explicit on/off choice.
|
|
36
|
+
* Older configs with a persisted language or Edge voice are treated as manual.
|
|
37
|
+
*/
|
|
38
|
+
function initializeVoiceDefaults(config, language = inferProductLanguageFromEnvironment()) {
|
|
39
|
+
const before = JSON.stringify({
|
|
40
|
+
voice: config.voice,
|
|
41
|
+
tools: config.tools?.media?.audio,
|
|
42
|
+
tts: config.messages?.tts
|
|
43
|
+
});
|
|
44
|
+
const languageMode = config.voice?.languageMode ?? (hasLegacyManualLanguageSelection(config) ? "manual" : "auto");
|
|
45
|
+
config.voice = {
|
|
46
|
+
...config.voice,
|
|
47
|
+
languageMode,
|
|
48
|
+
language: config.voice?.language ?? language
|
|
49
|
+
};
|
|
50
|
+
config.tools = config.tools ?? {};
|
|
51
|
+
config.tools.media = config.tools.media ?? {};
|
|
52
|
+
const audio = config.tools.media.audio;
|
|
53
|
+
config.tools.media.audio = {
|
|
54
|
+
...audio,
|
|
55
|
+
enabled: audio?.enabled ?? true,
|
|
56
|
+
provider: audio?.provider ?? "xopc-local",
|
|
57
|
+
fallback: audio?.fallback ?? {
|
|
58
|
+
enabled: false,
|
|
59
|
+
order: ["xopc-local"]
|
|
60
|
+
},
|
|
61
|
+
providers: {
|
|
62
|
+
...audio?.providers ?? {},
|
|
63
|
+
"xopc-local": {
|
|
64
|
+
model: DEFAULT_LOCAL_VOICE_MODEL_ID,
|
|
65
|
+
...audio?.providers?.["xopc-local"] ?? {}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
config.messages = config.messages ?? {};
|
|
70
|
+
const tts = config.messages.tts;
|
|
71
|
+
config.messages.tts = {
|
|
72
|
+
...tts,
|
|
73
|
+
enabled: tts?.enabled ?? true,
|
|
74
|
+
provider: tts?.provider ?? "edge",
|
|
75
|
+
trigger: tts?.trigger ?? "inbound",
|
|
76
|
+
providers: {
|
|
77
|
+
...tts?.providers ?? {},
|
|
78
|
+
edge: {
|
|
79
|
+
enabled: true,
|
|
80
|
+
...tts?.providers?.edge ?? {}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
if (languageMode === "auto") applyAutomaticVoiceLanguage(config, language);
|
|
85
|
+
return before !== JSON.stringify({
|
|
86
|
+
voice: config.voice,
|
|
87
|
+
tools: config.tools?.media?.audio,
|
|
88
|
+
tts: config.messages?.tts
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** Apply product language only to providers whose language mapping is known. */
|
|
92
|
+
function applyAutomaticVoiceLanguage(config, language) {
|
|
93
|
+
if ((config.voice?.languageMode ?? "auto") !== "auto") return false;
|
|
94
|
+
const before = JSON.stringify({
|
|
95
|
+
voice: config.voice,
|
|
96
|
+
tools: config.tools?.media?.audio,
|
|
97
|
+
tts: config.messages?.tts
|
|
98
|
+
});
|
|
99
|
+
const profile = resolveVoiceLanguageProfile(language);
|
|
100
|
+
config.voice = {
|
|
101
|
+
...config.voice,
|
|
102
|
+
languageMode: "auto",
|
|
103
|
+
language
|
|
104
|
+
};
|
|
105
|
+
const audio = config.tools?.media?.audio;
|
|
106
|
+
if (audio?.provider === "xopc-local") audio.providers = {
|
|
107
|
+
...audio.providers ?? {},
|
|
108
|
+
"xopc-local": {
|
|
109
|
+
...audio.providers?.["xopc-local"] ?? {},
|
|
110
|
+
model: DEFAULT_LOCAL_VOICE_MODEL_ID,
|
|
111
|
+
language: profile.sttLanguage
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const tts = config.messages?.tts;
|
|
115
|
+
if (tts?.provider === "edge") tts.providers = {
|
|
116
|
+
...tts.providers ?? {},
|
|
117
|
+
edge: {
|
|
118
|
+
enabled: true,
|
|
119
|
+
...tts.providers?.edge ?? {},
|
|
120
|
+
voice: profile.edgeVoice,
|
|
121
|
+
lang: profile.edgeLanguage
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
return before !== JSON.stringify({
|
|
125
|
+
voice: config.voice,
|
|
126
|
+
tools: config.tools?.media?.audio,
|
|
127
|
+
tts: config.messages?.tts
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/** Start the model download without delaying gateway readiness. */
|
|
131
|
+
function prepareConfiguredLocalVoiceModel(config) {
|
|
132
|
+
const audio = config.tools?.media?.audio;
|
|
133
|
+
if (audio?.enabled === false || (audio?.provider ?? "xopc-local") !== "xopc-local") return;
|
|
134
|
+
const configuredModel = audio?.providers?.["xopc-local"]?.model;
|
|
135
|
+
const modelId = typeof configuredModel === "string" && configuredModel.trim() ? configuredModel.trim() : DEFAULT_LOCAL_VOICE_MODEL_ID;
|
|
136
|
+
if (isLocalVoiceModelInstalled(modelId)) return;
|
|
137
|
+
try {
|
|
138
|
+
startLocalVoiceModelInstall(modelId);
|
|
139
|
+
log.info({
|
|
140
|
+
modelId,
|
|
141
|
+
phase: "startup_prepare"
|
|
142
|
+
}, "Local voice model preparation started");
|
|
143
|
+
} catch (err) {
|
|
144
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
145
|
+
log.warn({
|
|
146
|
+
err,
|
|
147
|
+
modelId,
|
|
148
|
+
errorMessage,
|
|
149
|
+
phase: "startup_prepare"
|
|
150
|
+
}, `Local voice model preparation failed: ${errorMessage}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
export { applyAutomaticVoiceLanguage, inferProductLanguageFromEnvironment, initializeVoiceDefaults, prepareConfiguredLocalVoiceModel, resolveVoiceLanguageProfile };
|
|
155
|
+
|
|
156
|
+
//# sourceMappingURL=language-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-profile.js","names":[],"sources":["../../../src/voice/language-profile.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { createLogger } from '../utils/logger.js';\nimport { startLocalVoiceModelInstall } from './local/model-manager.js';\nimport {\n DEFAULT_LOCAL_VOICE_MODEL_ID,\n isLocalVoiceModelInstalled,\n} from './local/models.js';\n\nconst log = createLogger('Voice:Language');\n\nexport type ProductLanguage = 'en' | 'zh';\nexport type VoiceLanguageMode = 'auto' | 'manual';\n\nexport interface VoiceLanguageProfile {\n language: ProductLanguage;\n sttLanguage: 'auto' | 'en';\n edgeLanguage: 'en-US' | 'zh-CN';\n edgeVoice: 'en-US-MichelleNeural' | 'zh-CN-XiaoxiaoNeural';\n}\n\nexport function resolveVoiceLanguageProfile(language: ProductLanguage): VoiceLanguageProfile {\n return language === 'zh'\n ? {\n language,\n sttLanguage: 'auto',\n edgeLanguage: 'zh-CN',\n edgeVoice: 'zh-CN-XiaoxiaoNeural',\n }\n : {\n language,\n sttLanguage: 'en',\n edgeLanguage: 'en-US',\n edgeVoice: 'en-US-MichelleNeural',\n };\n}\n\nexport function inferProductLanguageFromEnvironment(): ProductLanguage {\n const locale = [\n process.env.LC_ALL,\n process.env.LC_MESSAGES,\n process.env.LANG,\n Intl.DateTimeFormat().resolvedOptions().locale,\n ].find((value) => typeof value === 'string' && value.trim());\n return locale?.trim().toLowerCase().startsWith('zh') ? 'zh' : 'en';\n}\n\nfunction hasLegacyManualLanguageSelection(config: Config): boolean {\n const local = config.tools?.media?.audio?.providers?.['xopc-local'];\n const edge = config.messages?.tts?.providers?.edge;\n return (\n (typeof local?.language === 'string' && local.language.trim().length > 0) ||\n (typeof edge?.voice === 'string' && edge.voice.trim().length > 0) ||\n (typeof edge?.lang === 'string' && edge.lang.trim().length > 0)\n );\n}\n\n/**\n * Install safe product defaults while preserving every explicit on/off choice.\n * Older configs with a persisted language or Edge voice are treated as manual.\n */\nexport function initializeVoiceDefaults(\n config: Config,\n language: ProductLanguage = inferProductLanguageFromEnvironment(),\n): boolean {\n const before = JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n const existingMode = config.voice?.languageMode;\n const languageMode: VoiceLanguageMode =\n existingMode ?? (hasLegacyManualLanguageSelection(config) ? 'manual' : 'auto');\n\n config.voice = {\n ...config.voice,\n languageMode,\n language: config.voice?.language ?? language,\n };\n\n config.tools = config.tools ?? {};\n config.tools.media = config.tools.media ?? {};\n const audio = config.tools.media.audio;\n config.tools.media.audio = {\n ...audio,\n enabled: audio?.enabled ?? true,\n provider: audio?.provider ?? 'xopc-local',\n fallback: audio?.fallback ?? { enabled: false, order: ['xopc-local'] },\n providers: {\n ...(audio?.providers ?? {}),\n 'xopc-local': {\n model: DEFAULT_LOCAL_VOICE_MODEL_ID,\n ...(audio?.providers?.['xopc-local'] ?? {}),\n },\n },\n };\n\n config.messages = config.messages ?? {};\n const tts = config.messages.tts;\n config.messages.tts = {\n ...tts,\n enabled: tts?.enabled ?? true,\n provider: tts?.provider ?? 'edge',\n trigger: tts?.trigger ?? 'inbound',\n providers: {\n ...(tts?.providers ?? {}),\n edge: {\n enabled: true,\n ...(tts?.providers?.edge ?? {}),\n },\n },\n };\n\n if (languageMode === 'auto') {\n applyAutomaticVoiceLanguage(config, language);\n }\n return before !== JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n}\n\n/** Apply product language only to providers whose language mapping is known. */\nexport function applyAutomaticVoiceLanguage(config: Config, language: ProductLanguage): boolean {\n if ((config.voice?.languageMode ?? 'auto') !== 'auto') return false;\n const before = JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n const profile = resolveVoiceLanguageProfile(language);\n\n config.voice = { ...config.voice, languageMode: 'auto', language };\n const audio = config.tools?.media?.audio;\n if (audio?.provider === 'xopc-local') {\n audio.providers = {\n ...(audio.providers ?? {}),\n 'xopc-local': {\n ...(audio.providers?.['xopc-local'] ?? {}),\n model: DEFAULT_LOCAL_VOICE_MODEL_ID,\n language: profile.sttLanguage,\n },\n };\n }\n const tts = config.messages?.tts;\n if (tts?.provider === 'edge') {\n tts.providers = {\n ...(tts.providers ?? {}),\n edge: {\n enabled: true,\n ...(tts.providers?.edge ?? {}),\n voice: profile.edgeVoice,\n lang: profile.edgeLanguage,\n },\n };\n }\n return before !== JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n}\n\n/** Start the model download without delaying gateway readiness. */\nexport function prepareConfiguredLocalVoiceModel(config: Config): void {\n const audio = config.tools?.media?.audio;\n if (audio?.enabled === false || (audio?.provider ?? 'xopc-local') !== 'xopc-local') return;\n const configuredModel = audio?.providers?.['xopc-local']?.model;\n const modelId = typeof configuredModel === 'string' && configuredModel.trim()\n ? configuredModel.trim()\n : DEFAULT_LOCAL_VOICE_MODEL_ID;\n if (isLocalVoiceModelInstalled(modelId)) return;\n try {\n startLocalVoiceModelInstall(modelId);\n log.info({ modelId, phase: 'startup_prepare' }, 'Local voice model preparation started');\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, modelId, errorMessage, phase: 'startup_prepare' }, `Local voice model preparation failed: ${errorMessage}`);\n }\n}\n"],"mappings":";;;;;aACkD;AAOlD,MAAM,MAAM,aAAa,iBAAiB;AAY1C,SAAgB,4BAA4B,UAAiD;AAC3F,QAAO,aAAa,OAChB;EACE;EACA,aAAa;EACb,cAAc;EACd,WAAW;EACZ,GACD;EACE;EACA,aAAa;EACb,cAAc;EACd,WAAW;EACZ;;AAGP,SAAgB,sCAAuD;AAOrE,QANe;EACb,QAAQ,IAAI;EACZ,QAAQ,IAAI;EACZ,QAAQ,IAAI;EACZ,KAAK,gBAAgB,CAAC,iBAAiB,CAAC;EACzC,CAAC,MAAM,UAAU,OAAO,UAAU,YAAY,MAAM,MAAM,CAC9C,EAAE,MAAM,CAAC,aAAa,CAAC,WAAW,KAAK,GAAG,OAAO;;AAGhE,SAAS,iCAAiC,QAAyB;CACjE,MAAM,QAAQ,OAAO,OAAO,OAAO,OAAO,YAAY;CACtD,MAAM,OAAO,OAAO,UAAU,KAAK,WAAW;AAC9C,QACG,OAAO,OAAO,aAAa,YAAY,MAAM,SAAS,MAAM,CAAC,SAAS,KACtE,OAAO,MAAM,UAAU,YAAY,KAAK,MAAM,MAAM,CAAC,SAAS,KAC9D,OAAO,MAAM,SAAS,YAAY,KAAK,KAAK,MAAM,CAAC,SAAS;;;;;;AAQjE,SAAgB,wBACd,QACA,WAA4B,qCAAqC,EACxD;CACT,MAAM,SAAS,KAAK,UAAU;EAAE,OAAO,OAAO;EAAO,OAAO,OAAO,OAAO,OAAO;EAAO,KAAK,OAAO,UAAU;EAAK,CAAC;CAEpH,MAAM,eADe,OAAO,OAAO,iBAEhB,iCAAiC,OAAO,GAAG,WAAW;AAEzE,QAAO,QAAQ;EACb,GAAG,OAAO;EACV;EACA,UAAU,OAAO,OAAO,YAAY;EACrC;AAED,QAAO,QAAQ,OAAO,SAAS,EAAE;AACjC,QAAO,MAAM,QAAQ,OAAO,MAAM,SAAS,EAAE;CAC7C,MAAM,QAAQ,OAAO,MAAM,MAAM;AACjC,QAAO,MAAM,MAAM,QAAQ;EACzB,GAAG;EACH,SAAS,OAAO,WAAW;EAC3B,UAAU,OAAO,YAAY;EAC7B,UAAU,OAAO,YAAY;GAAE,SAAS;GAAO,OAAO,CAAC,aAAa;GAAE;EACtE,WAAW;GACT,GAAI,OAAO,aAAa,EAAE;GAC1B,cAAc;IACZ,OAAO;IACP,GAAI,OAAO,YAAY,iBAAiB,EAAE;IAC3C;GACF;EACF;AAED,QAAO,WAAW,OAAO,YAAY,EAAE;CACvC,MAAM,MAAM,OAAO,SAAS;AAC5B,QAAO,SAAS,MAAM;EACpB,GAAG;EACH,SAAS,KAAK,WAAW;EACzB,UAAU,KAAK,YAAY;EAC3B,SAAS,KAAK,WAAW;EACzB,WAAW;GACT,GAAI,KAAK,aAAa,EAAE;GACxB,MAAM;IACJ,SAAS;IACT,GAAI,KAAK,WAAW,QAAQ,EAAE;IAC/B;GACF;EACF;AAED,KAAI,iBAAiB,OACnB,6BAA4B,QAAQ,SAAS;AAE/C,QAAO,WAAW,KAAK,UAAU;EAAE,OAAO,OAAO;EAAO,OAAO,OAAO,OAAO,OAAO;EAAO,KAAK,OAAO,UAAU;EAAK,CAAC;;;AAIzH,SAAgB,4BAA4B,QAAgB,UAAoC;AAC9F,MAAK,OAAO,OAAO,gBAAgB,YAAY,OAAQ,QAAO;CAC9D,MAAM,SAAS,KAAK,UAAU;EAAE,OAAO,OAAO;EAAO,OAAO,OAAO,OAAO,OAAO;EAAO,KAAK,OAAO,UAAU;EAAK,CAAC;CACpH,MAAM,UAAU,4BAA4B,SAAS;AAErD,QAAO,QAAQ;EAAE,GAAG,OAAO;EAAO,cAAc;EAAQ;EAAU;CAClE,MAAM,QAAQ,OAAO,OAAO,OAAO;AACnC,KAAI,OAAO,aAAa,aACtB,OAAM,YAAY;EAChB,GAAI,MAAM,aAAa,EAAE;EACzB,cAAc;GACZ,GAAI,MAAM,YAAY,iBAAiB,EAAE;GACzC,OAAO;GACP,UAAU,QAAQ;GACnB;EACF;CAEH,MAAM,MAAM,OAAO,UAAU;AAC7B,KAAI,KAAK,aAAa,OACpB,KAAI,YAAY;EACd,GAAI,IAAI,aAAa,EAAE;EACvB,MAAM;GACJ,SAAS;GACT,GAAI,IAAI,WAAW,QAAQ,EAAE;GAC7B,OAAO,QAAQ;GACf,MAAM,QAAQ;GACf;EACF;AAEH,QAAO,WAAW,KAAK,UAAU;EAAE,OAAO,OAAO;EAAO,OAAO,OAAO,OAAO,OAAO;EAAO,KAAK,OAAO,UAAU;EAAK,CAAC;;;AAIzH,SAAgB,iCAAiC,QAAsB;CACrE,MAAM,QAAQ,OAAO,OAAO,OAAO;AACnC,KAAI,OAAO,YAAY,UAAU,OAAO,YAAY,kBAAkB,aAAc;CACpF,MAAM,kBAAkB,OAAO,YAAY,eAAe;CAC1D,MAAM,UAAU,OAAO,oBAAoB,YAAY,gBAAgB,MAAM,GACzE,gBAAgB,MAAM,GACtB;AACJ,KAAI,2BAA2B,QAAQ,CAAE;AACzC,KAAI;AACF,8BAA4B,QAAQ;AACpC,MAAI,KAAK;GAAE;GAAS,OAAO;GAAmB,EAAE,wCAAwC;UACjF,KAAK;EACZ,MAAM,eAAe,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AACrE,MAAI,KAAK;GAAE;GAAK;GAAS;GAAc,OAAO;GAAmB,EAAE,yCAAyC,eAAe"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LocalVoiceModelDefinition } from './models.js';
|
|
2
|
+
export interface LocalVoiceFileProgress {
|
|
3
|
+
status: 'download';
|
|
4
|
+
file: string;
|
|
5
|
+
loaded: number;
|
|
6
|
+
total: number;
|
|
7
|
+
progress: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function downloadLocalVoiceModelFiles(options: {
|
|
10
|
+
model: LocalVoiceModelDefinition;
|
|
11
|
+
destinationDir: string;
|
|
12
|
+
remoteHosts: readonly string[];
|
|
13
|
+
onProgress?: (progress: LocalVoiceFileProgress) => void;
|
|
14
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { mkdir, rm, stat } from "node:fs/promises";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
6
|
+
import { pipeline } from "node:stream/promises";
|
|
7
|
+
//#region src/voice/local/model-files.ts
|
|
8
|
+
function resolveModelFileUrl(remoteHost, model, file) {
|
|
9
|
+
const base = remoteHost.endsWith("/") ? remoteHost : `${remoteHost}/`;
|
|
10
|
+
const path = file.path.split("/").map(encodeURIComponent).join("/");
|
|
11
|
+
return new URL(`${model.repository}/resolve/${model.revision}/${path}`, base).toString();
|
|
12
|
+
}
|
|
13
|
+
async function sha256File(path) {
|
|
14
|
+
const hash = createHash("sha256");
|
|
15
|
+
for await (const chunk of createReadStream(path)) hash.update(chunk);
|
|
16
|
+
return hash.digest("hex");
|
|
17
|
+
}
|
|
18
|
+
async function downloadFile(url, destination, expectedBytes, onBytes) {
|
|
19
|
+
const existing = await stat(destination).catch(() => null);
|
|
20
|
+
let offset = existing?.isFile() ? existing.size : 0;
|
|
21
|
+
if (offset > expectedBytes) {
|
|
22
|
+
await rm(destination, { force: true });
|
|
23
|
+
offset = 0;
|
|
24
|
+
}
|
|
25
|
+
if (offset === expectedBytes) {
|
|
26
|
+
onBytes(offset);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const response = await fetch(url, {
|
|
30
|
+
redirect: "follow",
|
|
31
|
+
headers: offset > 0 ? { Range: `bytes=${offset}-` } : void 0
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok || !response.body) throw new Error(`Model download failed with HTTP ${response.status}`);
|
|
34
|
+
const isResume = offset > 0 && response.status === 206;
|
|
35
|
+
if (offset > 0 && !isResume) offset = 0;
|
|
36
|
+
const declaredBytes = Number(response.headers.get("content-length"));
|
|
37
|
+
const expectedResponseBytes = expectedBytes - offset;
|
|
38
|
+
if (Number.isFinite(declaredBytes) && declaredBytes > 0 && declaredBytes !== expectedResponseBytes) throw new Error(`Model download size mismatch: expected ${expectedResponseBytes}, received ${declaredBytes}`);
|
|
39
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
40
|
+
let loaded = offset;
|
|
41
|
+
let lastReportedBytes = offset;
|
|
42
|
+
let lastReportedAt = 0;
|
|
43
|
+
const source = Readable.fromWeb(response.body);
|
|
44
|
+
source.on("data", (chunk) => {
|
|
45
|
+
loaded += chunk.length;
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
if (loaded === expectedBytes || loaded - lastReportedBytes >= 256 * 1024 || now - lastReportedAt >= 250) {
|
|
48
|
+
lastReportedBytes = loaded;
|
|
49
|
+
lastReportedAt = now;
|
|
50
|
+
onBytes(loaded);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
await pipeline(source, createWriteStream(destination, {
|
|
54
|
+
flags: isResume ? "a" : "w",
|
|
55
|
+
mode: 384
|
|
56
|
+
}));
|
|
57
|
+
if (loaded !== expectedBytes) throw new Error(`Model download was incomplete: expected ${expectedBytes}, received ${loaded}`);
|
|
58
|
+
}
|
|
59
|
+
async function verifyModelFile(path, file) {
|
|
60
|
+
if ((await stat(path)).size !== file.bytes) throw new Error(`Model file size mismatch for ${file.path}`);
|
|
61
|
+
if (await sha256File(path) !== file.sha256) throw new Error(`Model file checksum mismatch for ${file.path}`);
|
|
62
|
+
}
|
|
63
|
+
async function downloadLocalVoiceModelFiles(options) {
|
|
64
|
+
const files = options.model.files;
|
|
65
|
+
if (!files?.length) throw new Error(`No downloadable files declared for ${options.model.id}`);
|
|
66
|
+
const total = files.reduce((sum, file) => sum + file.bytes, 0);
|
|
67
|
+
let completedBytes = 0;
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
const destination = join(options.destinationDir, file.path);
|
|
70
|
+
let lastError;
|
|
71
|
+
for (const host of options.remoteHosts) try {
|
|
72
|
+
await downloadFile(resolveModelFileUrl(host, options.model, file), destination, file.bytes, (fileBytes) => options.onProgress?.({
|
|
73
|
+
status: "download",
|
|
74
|
+
file: file.path,
|
|
75
|
+
loaded: completedBytes + fileBytes,
|
|
76
|
+
total,
|
|
77
|
+
progress: (completedBytes + fileBytes) / total
|
|
78
|
+
}));
|
|
79
|
+
await verifyModelFile(destination, file);
|
|
80
|
+
lastError = void 0;
|
|
81
|
+
break;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
lastError = error;
|
|
84
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
85
|
+
if (message.includes("checksum mismatch") || message.includes("size mismatch")) await rm(destination, { force: true });
|
|
86
|
+
}
|
|
87
|
+
if (lastError) throw lastError;
|
|
88
|
+
completedBytes += file.bytes;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { downloadLocalVoiceModelFiles };
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=model-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-files.js","names":[],"sources":["../../../../src/voice/local/model-files.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { createReadStream, createWriteStream } from 'node:fs';\nimport { mkdir, rm, stat } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\n\nimport type { LocalVoiceModelDefinition, LocalVoiceModelFile } from './models.js';\n\nexport interface LocalVoiceFileProgress {\n status: 'download';\n file: string;\n loaded: number;\n total: number;\n progress: number;\n}\n\nfunction resolveModelFileUrl(\n remoteHost: string,\n model: LocalVoiceModelDefinition,\n file: LocalVoiceModelFile,\n): string {\n const base = remoteHost.endsWith('/') ? remoteHost : `${remoteHost}/`;\n const path = file.path.split('/').map(encodeURIComponent).join('/');\n return new URL(`${model.repository}/resolve/${model.revision}/${path}`, base).toString();\n}\n\nasync function sha256File(path: string): Promise<string> {\n const hash = createHash('sha256');\n for await (const chunk of createReadStream(path)) hash.update(chunk as Buffer);\n return hash.digest('hex');\n}\n\nasync function downloadFile(\n url: string,\n destination: string,\n expectedBytes: number,\n onBytes: (bytes: number) => void,\n): Promise<void> {\n const existing = await stat(destination).catch(() => null);\n let offset = existing?.isFile() ? existing.size : 0;\n if (offset > expectedBytes) {\n await rm(destination, { force: true });\n offset = 0;\n }\n if (offset === expectedBytes) {\n onBytes(offset);\n return;\n }\n const response = await fetch(url, {\n redirect: 'follow',\n headers: offset > 0 ? { Range: `bytes=${offset}-` } : undefined,\n });\n if (!response.ok || !response.body) {\n throw new Error(`Model download failed with HTTP ${response.status}`);\n }\n const isResume = offset > 0 && response.status === 206;\n if (offset > 0 && !isResume) offset = 0;\n const declaredBytes = Number(response.headers.get('content-length'));\n const expectedResponseBytes = expectedBytes - offset;\n if (Number.isFinite(declaredBytes) && declaredBytes > 0 && declaredBytes !== expectedResponseBytes) {\n throw new Error(`Model download size mismatch: expected ${expectedResponseBytes}, received ${declaredBytes}`);\n }\n\n await mkdir(dirname(destination), { recursive: true });\n let loaded = offset;\n let lastReportedBytes = offset;\n let lastReportedAt = 0;\n const source = Readable.fromWeb(response.body as never);\n source.on('data', (chunk: Buffer) => {\n loaded += chunk.length;\n const now = Date.now();\n if (loaded === expectedBytes || loaded - lastReportedBytes >= 256 * 1024 || now - lastReportedAt >= 250) {\n lastReportedBytes = loaded;\n lastReportedAt = now;\n onBytes(loaded);\n }\n });\n await pipeline(source, createWriteStream(destination, { flags: isResume ? 'a' : 'w', mode: 0o600 }));\n if (loaded !== expectedBytes) {\n throw new Error(`Model download was incomplete: expected ${expectedBytes}, received ${loaded}`);\n }\n}\n\nasync function verifyModelFile(path: string, file: LocalVoiceModelFile): Promise<void> {\n const info = await stat(path);\n if (info.size !== file.bytes) {\n throw new Error(`Model file size mismatch for ${file.path}`);\n }\n const actual = await sha256File(path);\n if (actual !== file.sha256) {\n throw new Error(`Model file checksum mismatch for ${file.path}`);\n }\n}\n\nexport async function downloadLocalVoiceModelFiles(options: {\n model: LocalVoiceModelDefinition;\n destinationDir: string;\n remoteHosts: readonly string[];\n onProgress?: (progress: LocalVoiceFileProgress) => void;\n}): Promise<void> {\n const files = options.model.files;\n if (!files?.length) throw new Error(`No downloadable files declared for ${options.model.id}`);\n const total = files.reduce((sum, file) => sum + file.bytes, 0);\n let completedBytes = 0;\n\n for (const file of files) {\n const destination = join(options.destinationDir, file.path);\n let lastError: unknown;\n for (const host of options.remoteHosts) {\n try {\n await downloadFile(\n resolveModelFileUrl(host, options.model, file),\n destination,\n file.bytes,\n (fileBytes) => options.onProgress?.({\n status: 'download',\n file: file.path,\n loaded: completedBytes + fileBytes,\n total,\n progress: (completedBytes + fileBytes) / total,\n }),\n );\n await verifyModelFile(destination, file);\n lastError = undefined;\n break;\n } catch (error) {\n lastError = error;\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes('checksum mismatch') || message.includes('size mismatch')) {\n await rm(destination, { force: true });\n }\n }\n }\n if (lastError) {\n throw lastError;\n }\n completedBytes += file.bytes;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type LocalVoiceModelState = 'not_installed' | 'downloading' | 'ready' | 'error';
|
|
2
|
+
export interface LocalVoiceModelStatus {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
approximateBytes: number;
|
|
7
|
+
engine: string;
|
|
8
|
+
languages: readonly string[];
|
|
9
|
+
recommended?: boolean;
|
|
10
|
+
state: LocalVoiceModelState;
|
|
11
|
+
progress?: number;
|
|
12
|
+
downloadedBytes?: number;
|
|
13
|
+
totalBytes?: number;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function listLocalVoiceModelStatuses(): Promise<LocalVoiceModelStatus[]>;
|
|
17
|
+
export declare function startLocalVoiceModelInstall(modelId: string): LocalVoiceModelStatus;
|
|
18
|
+
export declare function removeLocalVoiceModel(modelId: string): Promise<void>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createLogger } from "../../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../../utils/logger.js";
|
|
3
|
+
import { LOCAL_VOICE_MODELS, getLocalVoiceModel, isLocalVoiceModelInstalled, resolveLocalVoiceModelDir, resolveLocalVoiceModelMarkerPath } from "./models.js";
|
|
4
|
+
import { getLocalVoiceRuntimeClient } from "./runtime-client.js";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
7
|
+
//#region src/voice/local/model-manager.ts
|
|
8
|
+
init_logger();
|
|
9
|
+
const log = createLogger("LocalVoice:Models");
|
|
10
|
+
const jobs = /* @__PURE__ */ new Map();
|
|
11
|
+
function progressFraction(progress) {
|
|
12
|
+
if (typeof progress.progress === "number" && Number.isFinite(progress.progress)) return progress.progress > 1 ? progress.progress / 100 : progress.progress;
|
|
13
|
+
if (typeof progress.loaded === "number" && typeof progress.total === "number" && progress.total > 0) return progress.loaded / progress.total;
|
|
14
|
+
}
|
|
15
|
+
async function listLocalVoiceModelStatuses() {
|
|
16
|
+
return Promise.all(LOCAL_VOICE_MODELS.map(async (model) => {
|
|
17
|
+
const active = jobs.get(model.id);
|
|
18
|
+
if (active) return { ...active };
|
|
19
|
+
if (!isLocalVoiceModelInstalled(model.id)) return {
|
|
20
|
+
id: model.id,
|
|
21
|
+
name: model.name,
|
|
22
|
+
description: model.description,
|
|
23
|
+
approximateBytes: model.approximateBytes,
|
|
24
|
+
engine: model.engine,
|
|
25
|
+
languages: model.languages,
|
|
26
|
+
recommended: model.recommended,
|
|
27
|
+
state: "not_installed"
|
|
28
|
+
};
|
|
29
|
+
let installedAt;
|
|
30
|
+
try {
|
|
31
|
+
installedAt = JSON.parse(await readFile(resolveLocalVoiceModelMarkerPath(model.id), "utf8")).installedAt;
|
|
32
|
+
} catch {}
|
|
33
|
+
return {
|
|
34
|
+
id: model.id,
|
|
35
|
+
name: model.name,
|
|
36
|
+
description: installedAt ? `${model.description} Installed ${installedAt}.` : model.description,
|
|
37
|
+
approximateBytes: model.approximateBytes,
|
|
38
|
+
engine: model.engine,
|
|
39
|
+
languages: model.languages,
|
|
40
|
+
recommended: model.recommended,
|
|
41
|
+
state: "ready"
|
|
42
|
+
};
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
function startLocalVoiceModelInstall(modelId) {
|
|
46
|
+
const model = getLocalVoiceModel(modelId);
|
|
47
|
+
const existing = jobs.get(model.id);
|
|
48
|
+
if (existing?.state === "downloading") return { ...existing };
|
|
49
|
+
const status = {
|
|
50
|
+
id: model.id,
|
|
51
|
+
name: model.name,
|
|
52
|
+
description: model.description,
|
|
53
|
+
approximateBytes: model.approximateBytes,
|
|
54
|
+
engine: model.engine,
|
|
55
|
+
languages: model.languages,
|
|
56
|
+
recommended: model.recommended,
|
|
57
|
+
state: "downloading",
|
|
58
|
+
progress: 0
|
|
59
|
+
};
|
|
60
|
+
jobs.set(model.id, status);
|
|
61
|
+
(async () => {
|
|
62
|
+
const modelDir = resolveLocalVoiceModelDir(model.id);
|
|
63
|
+
const stagingDir = `${modelDir}.download`;
|
|
64
|
+
try {
|
|
65
|
+
if (model.engine === "transformers.js") await rm(stagingDir, {
|
|
66
|
+
recursive: true,
|
|
67
|
+
force: true
|
|
68
|
+
});
|
|
69
|
+
await mkdir(stagingDir, { recursive: true });
|
|
70
|
+
await getLocalVoiceRuntimeClient().request("model.install", {
|
|
71
|
+
modelId: model.id,
|
|
72
|
+
cacheDir: stagingDir
|
|
73
|
+
}, { onProgress: (progress) => {
|
|
74
|
+
const current = jobs.get(model.id);
|
|
75
|
+
if (!current) return;
|
|
76
|
+
current.progress = progressFraction(progress) ?? current.progress;
|
|
77
|
+
current.downloadedBytes = progress.loaded;
|
|
78
|
+
current.totalBytes = progress.total;
|
|
79
|
+
} });
|
|
80
|
+
await writeFile(join(stagingDir, "installed.json"), `${JSON.stringify({
|
|
81
|
+
modelId: model.id,
|
|
82
|
+
repository: model.repository,
|
|
83
|
+
revision: model.revision,
|
|
84
|
+
engine: model.engine,
|
|
85
|
+
...model.dtype ? { dtype: model.dtype } : {},
|
|
86
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
87
|
+
}, null, 2)}\n`, { mode: 384 });
|
|
88
|
+
await rm(modelDir, {
|
|
89
|
+
recursive: true,
|
|
90
|
+
force: true
|
|
91
|
+
});
|
|
92
|
+
await rename(stagingDir, modelDir);
|
|
93
|
+
jobs.delete(model.id);
|
|
94
|
+
log.info({ modelId: model.id }, "Local voice model installed");
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (model.engine === "transformers.js") await rm(stagingDir, {
|
|
97
|
+
recursive: true,
|
|
98
|
+
force: true
|
|
99
|
+
}).catch(() => {});
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
+
jobs.set(model.id, {
|
|
102
|
+
...status,
|
|
103
|
+
state: "error",
|
|
104
|
+
error: message
|
|
105
|
+
});
|
|
106
|
+
log.error({
|
|
107
|
+
err: error,
|
|
108
|
+
modelId: model.id
|
|
109
|
+
}, `Local voice model installation failed: ${message}`);
|
|
110
|
+
}
|
|
111
|
+
})();
|
|
112
|
+
return { ...status };
|
|
113
|
+
}
|
|
114
|
+
async function removeLocalVoiceModel(modelId) {
|
|
115
|
+
const model = getLocalVoiceModel(modelId);
|
|
116
|
+
if (jobs.get(model.id)?.state === "downloading") throw new Error("Cannot remove a model while it is downloading");
|
|
117
|
+
try {
|
|
118
|
+
await getLocalVoiceRuntimeClient().request("model.unload", { modelId: model.id }, { timeoutMs: 1e4 });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
log.warn({
|
|
121
|
+
err: error,
|
|
122
|
+
modelId: model.id
|
|
123
|
+
}, "Local voice runtime unload failed; removing model files");
|
|
124
|
+
}
|
|
125
|
+
await rm(resolveLocalVoiceModelDir(model.id), {
|
|
126
|
+
recursive: true,
|
|
127
|
+
force: true
|
|
128
|
+
});
|
|
129
|
+
jobs.delete(model.id);
|
|
130
|
+
log.info({ modelId: model.id }, "Local voice model removed");
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { listLocalVoiceModelStatuses, removeLocalVoiceModel, startLocalVoiceModelInstall };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=model-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-manager.js","names":[],"sources":["../../../../src/voice/local/model-manager.ts"],"sourcesContent":["import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { createLogger } from '../../utils/logger.js';\nimport {\n LOCAL_VOICE_MODELS,\n getLocalVoiceModel,\n isLocalVoiceModelInstalled,\n resolveLocalVoiceModelDir,\n resolveLocalVoiceModelMarkerPath,\n} from './models.js';\nimport { getLocalVoiceRuntimeClient, type LocalVoiceRuntimeProgress } from './runtime-client.js';\n\nconst log = createLogger('LocalVoice:Models');\n\nexport type LocalVoiceModelState = 'not_installed' | 'downloading' | 'ready' | 'error';\n\nexport interface LocalVoiceModelStatus {\n id: string;\n name: string;\n description: string;\n approximateBytes: number;\n engine: string;\n languages: readonly string[];\n recommended?: boolean;\n state: LocalVoiceModelState;\n progress?: number;\n downloadedBytes?: number;\n totalBytes?: number;\n error?: string;\n}\n\nconst jobs = new Map<string, LocalVoiceModelStatus>();\n\nfunction progressFraction(progress: LocalVoiceRuntimeProgress): number | undefined {\n if (typeof progress.progress === 'number' && Number.isFinite(progress.progress)) {\n return progress.progress > 1 ? progress.progress / 100 : progress.progress;\n }\n if (typeof progress.loaded === 'number' && typeof progress.total === 'number' && progress.total > 0) {\n return progress.loaded / progress.total;\n }\n return undefined;\n}\n\nexport async function listLocalVoiceModelStatuses(): Promise<LocalVoiceModelStatus[]> {\n return Promise.all(\n LOCAL_VOICE_MODELS.map(async (model) => {\n const active = jobs.get(model.id);\n if (active) return { ...active };\n if (!isLocalVoiceModelInstalled(model.id)) {\n return {\n id: model.id,\n name: model.name,\n description: model.description,\n approximateBytes: model.approximateBytes,\n engine: model.engine,\n languages: model.languages,\n recommended: model.recommended,\n state: 'not_installed' as const,\n };\n }\n let installedAt: string | undefined;\n try {\n const marker = JSON.parse(await readFile(resolveLocalVoiceModelMarkerPath(model.id), 'utf8')) as { installedAt?: string };\n installedAt = marker.installedAt;\n } catch {\n // The marker's presence is authoritative; metadata is best-effort.\n }\n return {\n id: model.id,\n name: model.name,\n description: installedAt ? `${model.description} Installed ${installedAt}.` : model.description,\n approximateBytes: model.approximateBytes,\n engine: model.engine,\n languages: model.languages,\n recommended: model.recommended,\n state: 'ready' as const,\n };\n }),\n );\n}\n\nexport function startLocalVoiceModelInstall(modelId: string): LocalVoiceModelStatus {\n const model = getLocalVoiceModel(modelId);\n const existing = jobs.get(model.id);\n if (existing?.state === 'downloading') return { ...existing };\n const status: LocalVoiceModelStatus = {\n id: model.id,\n name: model.name,\n description: model.description,\n approximateBytes: model.approximateBytes,\n engine: model.engine,\n languages: model.languages,\n recommended: model.recommended,\n state: 'downloading',\n progress: 0,\n };\n jobs.set(model.id, status);\n void (async () => {\n const modelDir = resolveLocalVoiceModelDir(model.id);\n const stagingDir = `${modelDir}.download`;\n try {\n if (model.engine === 'transformers.js') {\n await rm(stagingDir, { recursive: true, force: true });\n }\n await mkdir(stagingDir, { recursive: true });\n await getLocalVoiceRuntimeClient().request(\n 'model.install',\n { modelId: model.id, cacheDir: stagingDir },\n {\n onProgress: (progress) => {\n const current = jobs.get(model.id);\n if (!current) return;\n current.progress = progressFraction(progress) ?? current.progress;\n current.downloadedBytes = progress.loaded;\n current.totalBytes = progress.total;\n },\n },\n );\n await writeFile(\n join(stagingDir, 'installed.json'),\n `${JSON.stringify({\n modelId: model.id,\n repository: model.repository,\n revision: model.revision,\n engine: model.engine,\n ...(model.dtype ? { dtype: model.dtype } : {}),\n installedAt: new Date().toISOString(),\n }, null, 2)}\\n`,\n { mode: 0o600 },\n );\n await rm(modelDir, { recursive: true, force: true });\n await rename(stagingDir, modelDir);\n jobs.delete(model.id);\n log.info({ modelId: model.id }, 'Local voice model installed');\n } catch (error) {\n // Keep verified/partial sherpa model files so a retry can continue with HTTP Range.\n if (model.engine === 'transformers.js') {\n await rm(stagingDir, { recursive: true, force: true }).catch(() => {});\n }\n const message = error instanceof Error ? error.message : String(error);\n jobs.set(model.id, { ...status, state: 'error', error: message });\n log.error({ err: error, modelId: model.id }, `Local voice model installation failed: ${message}`);\n }\n })();\n return { ...status };\n}\n\nexport async function removeLocalVoiceModel(modelId: string): Promise<void> {\n const model = getLocalVoiceModel(modelId);\n if (jobs.get(model.id)?.state === 'downloading') {\n throw new Error('Cannot remove a model while it is downloading');\n }\n try {\n await getLocalVoiceRuntimeClient().request('model.unload', { modelId: model.id }, { timeoutMs: 10_000 });\n } catch (error) {\n log.warn({ err: error, modelId: model.id }, 'Local voice runtime unload failed; removing model files');\n }\n await rm(resolveLocalVoiceModelDir(model.id), { recursive: true, force: true });\n jobs.delete(model.id);\n log.info({ modelId: model.id }, 'Local voice model removed');\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type LocalVoiceModelId = 'sensevoice-small' | 'tiny' | 'base' | 'small';
|
|
2
|
+
export type LocalVoiceEngine = 'sherpa-onnx' | 'transformers.js';
|
|
3
|
+
export interface LocalVoiceModelFile {
|
|
4
|
+
path: string;
|
|
5
|
+
bytes: number;
|
|
6
|
+
sha256: string;
|
|
7
|
+
}
|
|
8
|
+
export interface LocalVoiceModelDefinition {
|
|
9
|
+
id: LocalVoiceModelId;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
engine: LocalVoiceEngine;
|
|
13
|
+
repository: string;
|
|
14
|
+
revision: string;
|
|
15
|
+
dtype?: 'q8';
|
|
16
|
+
files?: readonly LocalVoiceModelFile[];
|
|
17
|
+
languages: readonly string[];
|
|
18
|
+
recommended?: boolean;
|
|
19
|
+
approximateBytes: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const DEFAULT_LOCAL_VOICE_MODEL_ID: LocalVoiceModelId;
|
|
22
|
+
export declare const LOCAL_VOICE_MODELS: readonly LocalVoiceModelDefinition[];
|
|
23
|
+
export declare function resolveLocalVoiceRootDir(): string;
|
|
24
|
+
export declare function resolveLocalVoiceModelDir(modelId: string): string;
|
|
25
|
+
export declare function resolveLocalVoiceModelMarkerPath(modelId: string): string;
|
|
26
|
+
export declare function getLocalVoiceModel(modelId: string | undefined): LocalVoiceModelDefinition;
|
|
27
|
+
export declare function isLocalVoiceModelInstalled(modelId: string): boolean;
|
|
28
|
+
export declare function hasInstalledLocalVoiceModel(): boolean;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { resolveStateDir } from "../../config/paths-state.js";
|
|
2
|
+
import { init_paths } from "../../config/paths.js";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
//#region src/voice/local/models.ts
|
|
6
|
+
init_paths();
|
|
7
|
+
const DEFAULT_LOCAL_VOICE_MODEL_ID = "sensevoice-small";
|
|
8
|
+
const LOCAL_VOICE_MODELS = [
|
|
9
|
+
{
|
|
10
|
+
id: "sensevoice-small",
|
|
11
|
+
name: "SenseVoice Small 中文",
|
|
12
|
+
description: "中文推荐;适合普通话、粤语和中英混说,支持标点与数字规整。",
|
|
13
|
+
engine: "sherpa-onnx",
|
|
14
|
+
repository: "csukuangfj/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17",
|
|
15
|
+
revision: "2365baeacb507f821a0c8120fcee3d484dba7a07",
|
|
16
|
+
files: [
|
|
17
|
+
{
|
|
18
|
+
path: "model.int8.onnx",
|
|
19
|
+
bytes: 239233841,
|
|
20
|
+
sha256: "c71f0ce00bec95b07744e116345e33d8cbbe08cef896382cf907bf4b51a2cd51"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
path: "tokens.txt",
|
|
24
|
+
bytes: 315894,
|
|
25
|
+
sha256: "f449eb28dc567533d7fa59be34e2abca8784f771850c78a47fb731a31429a1dc"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
path: "LICENSE",
|
|
29
|
+
bytes: 71,
|
|
30
|
+
sha256: "221c6df10b0931a5629adad671ea48fb7747e034c414b6d2bfa275bc3dd4ea17"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
languages: [
|
|
34
|
+
"zh",
|
|
35
|
+
"yue",
|
|
36
|
+
"en",
|
|
37
|
+
"ja",
|
|
38
|
+
"ko"
|
|
39
|
+
],
|
|
40
|
+
recommended: true,
|
|
41
|
+
approximateBytes: 239549806
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "tiny",
|
|
45
|
+
name: "Whisper Tiny",
|
|
46
|
+
description: "Fastest and smallest; best for short commands.",
|
|
47
|
+
engine: "transformers.js",
|
|
48
|
+
repository: "onnx-community/whisper-tiny",
|
|
49
|
+
revision: "ff4177021cc41f7db950912b73ea4fdf7d01d8e7",
|
|
50
|
+
dtype: "q8",
|
|
51
|
+
languages: ["multilingual"],
|
|
52
|
+
approximateBytes: 45 * 1024 * 1024
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "base",
|
|
56
|
+
name: "Whisper Base",
|
|
57
|
+
description: "Recommended balance of accuracy, speed, and disk use.",
|
|
58
|
+
engine: "transformers.js",
|
|
59
|
+
repository: "onnx-community/whisper-base",
|
|
60
|
+
revision: "1846881b6b3a3024392c1eea3ad983695bc23925",
|
|
61
|
+
dtype: "q8",
|
|
62
|
+
languages: ["multilingual"],
|
|
63
|
+
approximateBytes: 80 * 1024 * 1024
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "small",
|
|
67
|
+
name: "Whisper Small",
|
|
68
|
+
description: "Higher accuracy with more memory and disk use.",
|
|
69
|
+
engine: "transformers.js",
|
|
70
|
+
repository: "onnx-community/whisper-small",
|
|
71
|
+
revision: "36050c46d777d46dc4b5f43f6d90574fc38f8732",
|
|
72
|
+
dtype: "q8",
|
|
73
|
+
languages: ["multilingual"],
|
|
74
|
+
approximateBytes: 245 * 1024 * 1024
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
function resolveLocalVoiceRootDir() {
|
|
78
|
+
return process.env.XOPC_VOICE_MODEL_DIR?.trim() || join(resolveStateDir(), "voice");
|
|
79
|
+
}
|
|
80
|
+
function resolveLocalVoiceModelDir(modelId) {
|
|
81
|
+
return join(resolveLocalVoiceRootDir(), "models", modelId);
|
|
82
|
+
}
|
|
83
|
+
function resolveLocalVoiceModelMarkerPath(modelId) {
|
|
84
|
+
return join(resolveLocalVoiceModelDir(modelId), "installed.json");
|
|
85
|
+
}
|
|
86
|
+
function getLocalVoiceModel(modelId) {
|
|
87
|
+
const id = modelId?.trim() || "sensevoice-small";
|
|
88
|
+
const model = LOCAL_VOICE_MODELS.find((entry) => entry.id === id);
|
|
89
|
+
if (!model) throw new Error(`Unknown local voice model: ${id}`);
|
|
90
|
+
return model;
|
|
91
|
+
}
|
|
92
|
+
function isLocalVoiceModelInstalled(modelId) {
|
|
93
|
+
return existsSync(resolveLocalVoiceModelMarkerPath(modelId));
|
|
94
|
+
}
|
|
95
|
+
function hasInstalledLocalVoiceModel() {
|
|
96
|
+
return LOCAL_VOICE_MODELS.some((model) => isLocalVoiceModelInstalled(model.id));
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { DEFAULT_LOCAL_VOICE_MODEL_ID, LOCAL_VOICE_MODELS, getLocalVoiceModel, hasInstalledLocalVoiceModel, isLocalVoiceModelInstalled, resolveLocalVoiceModelDir, resolveLocalVoiceModelMarkerPath, resolveLocalVoiceRootDir };
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","names":[],"sources":["../../../../src/voice/local/models.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { resolveStateDir } from '../../config/paths.js';\n\nexport type LocalVoiceModelId = 'sensevoice-small' | 'tiny' | 'base' | 'small';\n\nexport type LocalVoiceEngine = 'sherpa-onnx' | 'transformers.js';\n\nexport interface LocalVoiceModelFile {\n path: string;\n bytes: number;\n sha256: string;\n}\n\nexport interface LocalVoiceModelDefinition {\n id: LocalVoiceModelId;\n name: string;\n description: string;\n engine: LocalVoiceEngine;\n repository: string;\n revision: string;\n dtype?: 'q8';\n files?: readonly LocalVoiceModelFile[];\n languages: readonly string[];\n recommended?: boolean;\n approximateBytes: number;\n}\n\nexport const DEFAULT_LOCAL_VOICE_MODEL_ID: LocalVoiceModelId = 'sensevoice-small';\n\nexport const LOCAL_VOICE_MODELS: readonly LocalVoiceModelDefinition[] = [\n {\n id: 'sensevoice-small',\n name: 'SenseVoice Small 中文',\n description: '中文推荐;适合普通话、粤语和中英混说,支持标点与数字规整。',\n engine: 'sherpa-onnx',\n repository: 'csukuangfj/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17',\n revision: '2365baeacb507f821a0c8120fcee3d484dba7a07',\n files: [\n {\n path: 'model.int8.onnx',\n bytes: 239_233_841,\n sha256: 'c71f0ce00bec95b07744e116345e33d8cbbe08cef896382cf907bf4b51a2cd51',\n },\n {\n path: 'tokens.txt',\n bytes: 315_894,\n sha256: 'f449eb28dc567533d7fa59be34e2abca8784f771850c78a47fb731a31429a1dc',\n },\n {\n path: 'LICENSE',\n bytes: 71,\n sha256: '221c6df10b0931a5629adad671ea48fb7747e034c414b6d2bfa275bc3dd4ea17',\n },\n ],\n languages: ['zh', 'yue', 'en', 'ja', 'ko'],\n recommended: true,\n approximateBytes: 239_549_806,\n },\n {\n id: 'tiny',\n name: 'Whisper Tiny',\n description: 'Fastest and smallest; best for short commands.',\n engine: 'transformers.js',\n repository: 'onnx-community/whisper-tiny',\n revision: 'ff4177021cc41f7db950912b73ea4fdf7d01d8e7',\n dtype: 'q8',\n languages: ['multilingual'],\n approximateBytes: 45 * 1024 * 1024,\n },\n {\n id: 'base',\n name: 'Whisper Base',\n description: 'Recommended balance of accuracy, speed, and disk use.',\n engine: 'transformers.js',\n repository: 'onnx-community/whisper-base',\n revision: '1846881b6b3a3024392c1eea3ad983695bc23925',\n dtype: 'q8',\n languages: ['multilingual'],\n approximateBytes: 80 * 1024 * 1024,\n },\n {\n id: 'small',\n name: 'Whisper Small',\n description: 'Higher accuracy with more memory and disk use.',\n engine: 'transformers.js',\n repository: 'onnx-community/whisper-small',\n revision: '36050c46d777d46dc4b5f43f6d90574fc38f8732',\n dtype: 'q8',\n languages: ['multilingual'],\n approximateBytes: 245 * 1024 * 1024,\n },\n] as const;\n\nexport function resolveLocalVoiceRootDir(): string {\n return process.env.XOPC_VOICE_MODEL_DIR?.trim() || join(resolveStateDir(), 'voice');\n}\n\nexport function resolveLocalVoiceModelDir(modelId: string): string {\n return join(resolveLocalVoiceRootDir(), 'models', modelId);\n}\n\nexport function resolveLocalVoiceModelMarkerPath(modelId: string): string {\n return join(resolveLocalVoiceModelDir(modelId), 'installed.json');\n}\n\nexport function getLocalVoiceModel(modelId: string | undefined): LocalVoiceModelDefinition {\n const id = modelId?.trim() || DEFAULT_LOCAL_VOICE_MODEL_ID;\n const model = LOCAL_VOICE_MODELS.find((entry) => entry.id === id);\n if (!model) {\n throw new Error(`Unknown local voice model: ${id}`);\n }\n return model;\n}\n\nexport function isLocalVoiceModelInstalled(modelId: string): boolean {\n return existsSync(resolveLocalVoiceModelMarkerPath(modelId));\n}\n\nexport function hasInstalledLocalVoiceModel(): boolean {\n return LOCAL_VOICE_MODELS.some((model) => isLocalVoiceModelInstalled(model.id));\n}\n"],"mappings":";;;;;YAGwD;AA0BxD,MAAa,+BAAkD;AAE/D,MAAa,qBAA2D;CACtE;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,OAAO;GACL;IACE,MAAM;IACN,OAAO;IACP,QAAQ;IACT;GACD;IACE,MAAM;IACN,OAAO;IACP,QAAQ;IACT;GACD;IACE,MAAM;IACN,OAAO;IACP,QAAQ;IACT;GACF;EACD,WAAW;GAAC;GAAM;GAAO;GAAM;GAAM;GAAK;EAC1C,aAAa;EACb,kBAAkB;EACnB;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,OAAO;EACP,WAAW,CAAC,eAAe;EAC3B,kBAAkB,KAAK,OAAO;EAC/B;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,OAAO;EACP,WAAW,CAAC,eAAe;EAC3B,kBAAkB,KAAK,OAAO;EAC/B;CACD;EACE,IAAI;EACJ,MAAM;EACN,aAAa;EACb,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,OAAO;EACP,WAAW,CAAC,eAAe;EAC3B,kBAAkB,MAAM,OAAO;EAChC;CACF;AAED,SAAgB,2BAAmC;AACjD,QAAO,QAAQ,IAAI,sBAAsB,MAAM,IAAI,KAAK,iBAAiB,EAAE,QAAQ;;AAGrF,SAAgB,0BAA0B,SAAyB;AACjE,QAAO,KAAK,0BAA0B,EAAE,UAAU,QAAQ;;AAG5D,SAAgB,iCAAiC,SAAyB;AACxE,QAAO,KAAK,0BAA0B,QAAQ,EAAE,iBAAiB;;AAGnE,SAAgB,mBAAmB,SAAwD;CACzF,MAAM,KAAK,SAAS,MAAM,IAAA;CAC1B,MAAM,QAAQ,mBAAmB,MAAM,UAAU,MAAM,OAAO,GAAG;AACjE,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,8BAA8B,KAAK;AAErD,QAAO;;AAGT,SAAgB,2BAA2B,SAA0B;AACnE,QAAO,WAAW,iCAAiC,QAAQ,CAAC;;AAG9D,SAAgB,8BAAuC;AACrD,QAAO,mBAAmB,MAAM,UAAU,2BAA2B,MAAM,GAAG,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface LocalVoiceRuntimeProgress {
|
|
2
|
+
status?: string;
|
|
3
|
+
file?: string;
|
|
4
|
+
progress?: number;
|
|
5
|
+
loaded?: number;
|
|
6
|
+
total?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class LocalVoiceRuntimeClient {
|
|
9
|
+
private child;
|
|
10
|
+
private nextId;
|
|
11
|
+
private pending;
|
|
12
|
+
request<T>(method: string, params?: Record<string, unknown>, options?: {
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
/** Health/status probes must never terminate an active model download on timeout. */
|
|
16
|
+
stopOnTimeout?: boolean;
|
|
17
|
+
onProgress?: (progress: LocalVoiceRuntimeProgress) => void;
|
|
18
|
+
}): Promise<T>;
|
|
19
|
+
stop(): void;
|
|
20
|
+
private ensureStarted;
|
|
21
|
+
private handleLine;
|
|
22
|
+
private rejectAll;
|
|
23
|
+
private deletePending;
|
|
24
|
+
}
|
|
25
|
+
export declare function getLocalVoiceRuntimeClient(): LocalVoiceRuntimeClient;
|
|
26
|
+
export declare function resetLocalVoiceRuntimeForTests(): void;
|