@nac3/forge-cli 0.2.0-alpha.1
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/LICENSE +45 -0
- package/README.md +371 -0
- package/dist/bin/yf.d.ts +5 -0
- package/dist/bin/yf.d.ts.map +1 -0
- package/dist/bin/yf.js +86 -0
- package/dist/bin/yf.js.map +1 -0
- package/dist/chat/claude.d.ts +100 -0
- package/dist/chat/claude.d.ts.map +1 -0
- package/dist/chat/claude.js +228 -0
- package/dist/chat/claude.js.map +1 -0
- package/dist/chat/ingest_session.d.ts +97 -0
- package/dist/chat/ingest_session.d.ts.map +1 -0
- package/dist/chat/ingest_session.js +99 -0
- package/dist/chat/ingest_session.js.map +1 -0
- package/dist/chat/panel.d.ts +15 -0
- package/dist/chat/panel.d.ts.map +1 -0
- package/dist/chat/panel.js +1526 -0
- package/dist/chat/panel.js.map +1 -0
- package/dist/chat/persistence.d.ts +37 -0
- package/dist/chat/persistence.d.ts.map +1 -0
- package/dist/chat/persistence.js +91 -0
- package/dist/chat/persistence.js.map +1 -0
- package/dist/chat/server.d.ts +34 -0
- package/dist/chat/server.d.ts.map +1 -0
- package/dist/chat/server.js +1540 -0
- package/dist/chat/server.js.map +1 -0
- package/dist/chat/spec_extract.d.ts +35 -0
- package/dist/chat/spec_extract.d.ts.map +1 -0
- package/dist/chat/spec_extract.js +152 -0
- package/dist/chat/spec_extract.js.map +1 -0
- package/dist/chat/spec_plan.d.ts +65 -0
- package/dist/chat/spec_plan.d.ts.map +1 -0
- package/dist/chat/spec_plan.js +160 -0
- package/dist/chat/spec_plan.js.map +1 -0
- package/dist/chat/spec_scaffold.d.ts +95 -0
- package/dist/chat/spec_scaffold.d.ts.map +1 -0
- package/dist/chat/spec_scaffold.js +220 -0
- package/dist/chat/spec_scaffold.js.map +1 -0
- package/dist/chat/tools/git.d.ts +59 -0
- package/dist/chat/tools/git.d.ts.map +1 -0
- package/dist/chat/tools/git.js +313 -0
- package/dist/chat/tools/git.js.map +1 -0
- package/dist/chat/tools/github.d.ts +59 -0
- package/dist/chat/tools/github.d.ts.map +1 -0
- package/dist/chat/tools/github.js +310 -0
- package/dist/chat/tools/github.js.map +1 -0
- package/dist/chat/tools/lifecycle.d.ts +82 -0
- package/dist/chat/tools/lifecycle.d.ts.map +1 -0
- package/dist/chat/tools/lifecycle.js +295 -0
- package/dist/chat/tools/lifecycle.js.map +1 -0
- package/dist/chat/tools/manual.d.ts +26 -0
- package/dist/chat/tools/manual.d.ts.map +1 -0
- package/dist/chat/tools/manual.js +164 -0
- package/dist/chat/tools/manual.js.map +1 -0
- package/dist/chat/tools/reader.d.ts +80 -0
- package/dist/chat/tools/reader.d.ts.map +1 -0
- package/dist/chat/tools/reader.js +471 -0
- package/dist/chat/tools/reader.js.map +1 -0
- package/dist/chat/tools.d.ts +106 -0
- package/dist/chat/tools.d.ts.map +1 -0
- package/dist/chat/tools.js +587 -0
- package/dist/chat/tools.js.map +1 -0
- package/dist/codegen/e2e.d.ts +106 -0
- package/dist/codegen/e2e.d.ts.map +1 -0
- package/dist/codegen/e2e.js +931 -0
- package/dist/codegen/e2e.js.map +1 -0
- package/dist/codegen/v3_flow_emit.d.ts +70 -0
- package/dist/codegen/v3_flow_emit.d.ts.map +1 -0
- package/dist/codegen/v3_flow_emit.js +225 -0
- package/dist/codegen/v3_flow_emit.js.map +1 -0
- package/dist/commands/_stub.d.ts +2 -0
- package/dist/commands/_stub.d.ts.map +1 -0
- package/dist/commands/_stub.js +21 -0
- package/dist/commands/_stub.js.map +1 -0
- package/dist/commands/app.d.ts +31 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +331 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/chat.d.ts +18 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +76 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/deploy.d.ts +21 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +121 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/doctor.d.ts +14 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +280 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/figma.d.ts +32 -0
- package/dist/commands/figma.d.ts.map +1 -0
- package/dist/commands/figma.js +141 -0
- package/dist/commands/figma.js.map +1 -0
- package/dist/commands/gen-flow-tests.d.ts +8 -0
- package/dist/commands/gen-flow-tests.d.ts.map +1 -0
- package/dist/commands/gen-flow-tests.js +78 -0
- package/dist/commands/gen-flow-tests.js.map +1 -0
- package/dist/commands/gen-tests.d.ts +9 -0
- package/dist/commands/gen-tests.d.ts.map +1 -0
- package/dist/commands/gen-tests.js +118 -0
- package/dist/commands/gen-tests.js.map +1 -0
- package/dist/commands/license.d.ts +14 -0
- package/dist/commands/license.d.ts.map +1 -0
- package/dist/commands/license.js +182 -0
- package/dist/commands/license.js.map +1 -0
- package/dist/commands/log.d.ts +19 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +101 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/migrate.d.ts +118 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +1410 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/mobile.d.ts +27 -0
- package/dist/commands/mobile.d.ts.map +1 -0
- package/dist/commands/mobile.js +90 -0
- package/dist/commands/mobile.js.map +1 -0
- package/dist/commands/new.d.ts +32 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +107 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/pilot.d.ts +8 -0
- package/dist/commands/pilot.d.ts.map +1 -0
- package/dist/commands/pilot.js +104 -0
- package/dist/commands/pilot.js.map +1 -0
- package/dist/commands/projects.d.ts +21 -0
- package/dist/commands/projects.d.ts.map +1 -0
- package/dist/commands/projects.js +238 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/publish.d.ts +35 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +194 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/repo.d.ts +59 -0
- package/dist/commands/repo.d.ts.map +1 -0
- package/dist/commands/repo.js +178 -0
- package/dist/commands/repo.js.map +1 -0
- package/dist/commands/review-screens.d.ts +28 -0
- package/dist/commands/review-screens.d.ts.map +1 -0
- package/dist/commands/review-screens.js +345 -0
- package/dist/commands/review-screens.js.map +1 -0
- package/dist/commands/scenarios.d.ts +23 -0
- package/dist/commands/scenarios.d.ts.map +1 -0
- package/dist/commands/scenarios.js +304 -0
- package/dist/commands/scenarios.js.map +1 -0
- package/dist/commands/ship.d.ts +18 -0
- package/dist/commands/ship.d.ts.map +1 -0
- package/dist/commands/ship.js +41 -0
- package/dist/commands/ship.js.map +1 -0
- package/dist/commands/test.d.ts +29 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +62 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/tunnel.d.ts +22 -0
- package/dist/commands/tunnel.d.ts.map +1 -0
- package/dist/commands/tunnel.js +77 -0
- package/dist/commands/tunnel.js.map +1 -0
- package/dist/commands/validate.d.ts +14 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +51 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/vault.d.ts +32 -0
- package/dist/commands/vault.d.ts.map +1 -0
- package/dist/commands/vault.js +489 -0
- package/dist/commands/vault.js.map +1 -0
- package/dist/commands/voice.d.ts +16 -0
- package/dist/commands/voice.d.ts.map +1 -0
- package/dist/commands/voice.js +69 -0
- package/dist/commands/voice.js.map +1 -0
- package/dist/core/cascade_router.d.ts +90 -0
- package/dist/core/cascade_router.d.ts.map +1 -0
- package/dist/core/cascade_router.js +131 -0
- package/dist/core/cascade_router.js.map +1 -0
- package/dist/core/cf_tunnel.d.ts +52 -0
- package/dist/core/cf_tunnel.d.ts.map +1 -0
- package/dist/core/cf_tunnel.js +134 -0
- package/dist/core/cf_tunnel.js.map +1 -0
- package/dist/core/gha_dispatcher.d.ts +48 -0
- package/dist/core/gha_dispatcher.d.ts.map +1 -0
- package/dist/core/gha_dispatcher.js +198 -0
- package/dist/core/gha_dispatcher.js.map +1 -0
- package/dist/core/logger.d.ts +89 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +245 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/mode.d.ts +26 -0
- package/dist/core/mode.d.ts.map +1 -0
- package/dist/core/mode.js +122 -0
- package/dist/core/mode.js.map +1 -0
- package/dist/core/pairing.d.ts +40 -0
- package/dist/core/pairing.d.ts.map +1 -0
- package/dist/core/pairing.js +145 -0
- package/dist/core/pairing.js.map +1 -0
- package/dist/core/pilot_setup.d.ts +29 -0
- package/dist/core/pilot_setup.d.ts.map +1 -0
- package/dist/core/pilot_setup.js +119 -0
- package/dist/core/pilot_setup.js.map +1 -0
- package/dist/core/polar.d.ts +81 -0
- package/dist/core/polar.d.ts.map +1 -0
- package/dist/core/polar.js +175 -0
- package/dist/core/polar.js.map +1 -0
- package/dist/core/project_picker.d.ts +56 -0
- package/dist/core/project_picker.d.ts.map +1 -0
- package/dist/core/project_picker.js +86 -0
- package/dist/core/project_picker.js.map +1 -0
- package/dist/core/projects.d.ts +58 -0
- package/dist/core/projects.d.ts.map +1 -0
- package/dist/core/projects.js +146 -0
- package/dist/core/projects.js.map +1 -0
- package/dist/core/projects_sync.d.ts +80 -0
- package/dist/core/projects_sync.d.ts.map +1 -0
- package/dist/core/projects_sync.js +278 -0
- package/dist/core/projects_sync.js.map +1 -0
- package/dist/core/remote_runner.d.ts +70 -0
- package/dist/core/remote_runner.d.ts.map +1 -0
- package/dist/core/remote_runner.js +133 -0
- package/dist/core/remote_runner.js.map +1 -0
- package/dist/core/repo_state.d.ts +24 -0
- package/dist/core/repo_state.d.ts.map +1 -0
- package/dist/core/repo_state.js +109 -0
- package/dist/core/repo_state.js.map +1 -0
- package/dist/core/target.d.ts +31 -0
- package/dist/core/target.d.ts.map +1 -0
- package/dist/core/target.js +121 -0
- package/dist/core/target.js.map +1 -0
- package/dist/deploy/aws.d.ts +43 -0
- package/dist/deploy/aws.d.ts.map +1 -0
- package/dist/deploy/aws.js +173 -0
- package/dist/deploy/aws.js.map +1 -0
- package/dist/figma/api.d.ts +35 -0
- package/dist/figma/api.d.ts.map +1 -0
- package/dist/figma/api.js +40 -0
- package/dist/figma/api.js.map +1 -0
- package/dist/figma/decorator.d.ts +74 -0
- package/dist/figma/decorator.d.ts.map +1 -0
- package/dist/figma/decorator.js +210 -0
- package/dist/figma/decorator.js.map +1 -0
- package/dist/figma/heuristics.d.ts +29 -0
- package/dist/figma/heuristics.d.ts.map +1 -0
- package/dist/figma/heuristics.js +110 -0
- package/dist/figma/heuristics.js.map +1 -0
- package/dist/figma/normalize.d.ts +33 -0
- package/dist/figma/normalize.d.ts.map +1 -0
- package/dist/figma/normalize.js +101 -0
- package/dist/figma/normalize.js.map +1 -0
- package/dist/figma/tokens.d.ts +23 -0
- package/dist/figma/tokens.d.ts.map +1 -0
- package/dist/figma/tokens.js +111 -0
- package/dist/figma/tokens.js.map +1 -0
- package/dist/figma/types.d.ts +118 -0
- package/dist/figma/types.d.ts.map +1 -0
- package/dist/figma/types.js +12 -0
- package/dist/figma/types.js.map +1 -0
- package/dist/i18n/index.d.ts +48 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +135 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/types.d.ts +52 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +85 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/lan/mdns_packet.d.ts +74 -0
- package/dist/lan/mdns_packet.d.ts.map +1 -0
- package/dist/lan/mdns_packet.js +247 -0
- package/dist/lan/mdns_packet.js.map +1 -0
- package/dist/lan/mdns_service.d.ts +102 -0
- package/dist/lan/mdns_service.d.ts.map +1 -0
- package/dist/lan/mdns_service.js +206 -0
- package/dist/lan/mdns_service.js.map +1 -0
- package/dist/license/activate.d.ts +33 -0
- package/dist/license/activate.d.ts.map +1 -0
- package/dist/license/activate.js +135 -0
- package/dist/license/activate.js.map +1 -0
- package/dist/license/fingerprint.d.ts +2 -0
- package/dist/license/fingerprint.d.ts.map +1 -0
- package/dist/license/fingerprint.js +29 -0
- package/dist/license/fingerprint.js.map +1 -0
- package/dist/license/hito4_client.d.ts +24 -0
- package/dist/license/hito4_client.d.ts.map +1 -0
- package/dist/license/hito4_client.js +103 -0
- package/dist/license/hito4_client.js.map +1 -0
- package/dist/license/index.d.ts +22 -0
- package/dist/license/index.d.ts.map +1 -0
- package/dist/license/index.js +125 -0
- package/dist/license/index.js.map +1 -0
- package/dist/license/types.d.ts +38 -0
- package/dist/license/types.d.ts.map +1 -0
- package/dist/license/types.js +9 -0
- package/dist/license/types.js.map +1 -0
- package/dist/migrate/ai-apply.d.ts +198 -0
- package/dist/migrate/ai-apply.d.ts.map +1 -0
- package/dist/migrate/ai-apply.js +833 -0
- package/dist/migrate/ai-apply.js.map +1 -0
- package/dist/migrate/ai-decorator.d.ts +87 -0
- package/dist/migrate/ai-decorator.d.ts.map +1 -0
- package/dist/migrate/ai-decorator.js +203 -0
- package/dist/migrate/ai-decorator.js.map +1 -0
- package/dist/migrate/apply.d.ts +28 -0
- package/dist/migrate/apply.d.ts.map +1 -0
- package/dist/migrate/apply.js +119 -0
- package/dist/migrate/apply.js.map +1 -0
- package/dist/migrate/audit.d.ts +9 -0
- package/dist/migrate/audit.d.ts.map +1 -0
- package/dist/migrate/audit.js +197 -0
- package/dist/migrate/audit.js.map +1 -0
- package/dist/migrate/diff.d.ts +28 -0
- package/dist/migrate/diff.d.ts.map +1 -0
- package/dist/migrate/diff.js +154 -0
- package/dist/migrate/diff.js.map +1 -0
- package/dist/migrate/html-orchestrator.d.ts +81 -0
- package/dist/migrate/html-orchestrator.d.ts.map +1 -0
- package/dist/migrate/html-orchestrator.js +233 -0
- package/dist/migrate/html-orchestrator.js.map +1 -0
- package/dist/migrate/html-walker.d.ts +93 -0
- package/dist/migrate/html-walker.d.ts.map +1 -0
- package/dist/migrate/html-walker.js +288 -0
- package/dist/migrate/html-walker.js.map +1 -0
- package/dist/migrate/js-template-walker.d.ts +118 -0
- package/dist/migrate/js-template-walker.d.ts.map +1 -0
- package/dist/migrate/js-template-walker.js +644 -0
- package/dist/migrate/js-template-walker.js.map +1 -0
- package/dist/migrate/manifest-validator.d.ts +30 -0
- package/dist/migrate/manifest-validator.d.ts.map +1 -0
- package/dist/migrate/manifest-validator.js +261 -0
- package/dist/migrate/manifest-validator.js.map +1 -0
- package/dist/migrate/overrides.d.ts +58 -0
- package/dist/migrate/overrides.d.ts.map +1 -0
- package/dist/migrate/overrides.js +193 -0
- package/dist/migrate/overrides.js.map +1 -0
- package/dist/migrate/plugin-scope.d.ts +42 -0
- package/dist/migrate/plugin-scope.d.ts.map +1 -0
- package/dist/migrate/plugin-scope.js +94 -0
- package/dist/migrate/plugin-scope.js.map +1 -0
- package/dist/migrate/types.d.ts +45 -0
- package/dist/migrate/types.d.ts.map +1 -0
- package/dist/migrate/types.js +9 -0
- package/dist/migrate/types.js.map +1 -0
- package/dist/migrate/verb-inference.d.ts +37 -0
- package/dist/migrate/verb-inference.d.ts.map +1 -0
- package/dist/migrate/verb-inference.js +274 -0
- package/dist/migrate/verb-inference.js.map +1 -0
- package/dist/nac3/attrs.d.ts +87 -0
- package/dist/nac3/attrs.d.ts.map +1 -0
- package/dist/nac3/attrs.js +134 -0
- package/dist/nac3/attrs.js.map +1 -0
- package/dist/nac3/scenario_dsl.d.ts +71 -0
- package/dist/nac3/scenario_dsl.d.ts.map +1 -0
- package/dist/nac3/scenario_dsl.js +191 -0
- package/dist/nac3/scenario_dsl.js.map +1 -0
- package/dist/nac3/tokens.d.ts +126 -0
- package/dist/nac3/tokens.d.ts.map +1 -0
- package/dist/nac3/tokens.js +138 -0
- package/dist/nac3/tokens.js.map +1 -0
- package/dist/reader/parsers/csv.d.ts +42 -0
- package/dist/reader/parsers/csv.d.ts.map +1 -0
- package/dist/reader/parsers/csv.js +221 -0
- package/dist/reader/parsers/csv.js.map +1 -0
- package/dist/reader/parsers/docx.d.ts +31 -0
- package/dist/reader/parsers/docx.d.ts.map +1 -0
- package/dist/reader/parsers/docx.js +51 -0
- package/dist/reader/parsers/docx.js.map +1 -0
- package/dist/reader/parsers/epub.d.ts +39 -0
- package/dist/reader/parsers/epub.d.ts.map +1 -0
- package/dist/reader/parsers/epub.js +265 -0
- package/dist/reader/parsers/epub.js.map +1 -0
- package/dist/reader/parsers/html.d.ts +40 -0
- package/dist/reader/parsers/html.d.ts.map +1 -0
- package/dist/reader/parsers/html.js +386 -0
- package/dist/reader/parsers/html.js.map +1 -0
- package/dist/reader/parsers/md.d.ts +30 -0
- package/dist/reader/parsers/md.d.ts.map +1 -0
- package/dist/reader/parsers/md.js +199 -0
- package/dist/reader/parsers/md.js.map +1 -0
- package/dist/reader/parsers/pdf.d.ts +39 -0
- package/dist/reader/parsers/pdf.d.ts.map +1 -0
- package/dist/reader/parsers/pdf.js +220 -0
- package/dist/reader/parsers/pdf.js.map +1 -0
- package/dist/reader/parsers/rtf.d.ts +37 -0
- package/dist/reader/parsers/rtf.d.ts.map +1 -0
- package/dist/reader/parsers/rtf.js +347 -0
- package/dist/reader/parsers/rtf.js.map +1 -0
- package/dist/reader/parsers/source.d.ts +32 -0
- package/dist/reader/parsers/source.d.ts.map +1 -0
- package/dist/reader/parsers/source.js +122 -0
- package/dist/reader/parsers/source.js.map +1 -0
- package/dist/reader/parsers/txt.d.ts +25 -0
- package/dist/reader/parsers/txt.d.ts.map +1 -0
- package/dist/reader/parsers/txt.js +56 -0
- package/dist/reader/parsers/txt.js.map +1 -0
- package/dist/reader/parsers/xlsx.d.ts +33 -0
- package/dist/reader/parsers/xlsx.d.ts.map +1 -0
- package/dist/reader/parsers/xlsx.js +143 -0
- package/dist/reader/parsers/xlsx.js.map +1 -0
- package/dist/reader/registry.d.ts +39 -0
- package/dist/reader/registry.d.ts.map +1 -0
- package/dist/reader/registry.js +172 -0
- package/dist/reader/registry.js.map +1 -0
- package/dist/reader/search.d.ts +27 -0
- package/dist/reader/search.d.ts.map +1 -0
- package/dist/reader/search.js +77 -0
- package/dist/reader/search.js.map +1 -0
- package/dist/reader/state.d.ts +56 -0
- package/dist/reader/state.d.ts.map +1 -0
- package/dist/reader/state.js +179 -0
- package/dist/reader/state.js.map +1 -0
- package/dist/reader/types.d.ts +119 -0
- package/dist/reader/types.d.ts.map +1 -0
- package/dist/reader/types.js +23 -0
- package/dist/reader/types.js.map +1 -0
- package/dist/ship/run.d.ts +26 -0
- package/dist/ship/run.d.ts.map +1 -0
- package/dist/ship/run.js +123 -0
- package/dist/ship/run.js.map +1 -0
- package/dist/template/index.d.ts +50 -0
- package/dist/template/index.d.ts.map +1 -0
- package/dist/template/index.js +140 -0
- package/dist/template/index.js.map +1 -0
- package/dist/ui/colors.d.ts +13 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +26 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/validate/index.d.ts +19 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +181 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/vault/catalog.d.ts +55 -0
- package/dist/vault/catalog.d.ts.map +1 -0
- package/dist/vault/catalog.js +424 -0
- package/dist/vault/catalog.js.map +1 -0
- package/dist/vault/crypto.d.ts +82 -0
- package/dist/vault/crypto.d.ts.map +1 -0
- package/dist/vault/crypto.js +173 -0
- package/dist/vault/crypto.js.map +1 -0
- package/dist/vault/git_askpass.d.ts +26 -0
- package/dist/vault/git_askpass.d.ts.map +1 -0
- package/dist/vault/git_askpass.js +104 -0
- package/dist/vault/git_askpass.js.map +1 -0
- package/dist/vault/migrator.d.ts +57 -0
- package/dist/vault/migrator.d.ts.map +1 -0
- package/dist/vault/migrator.js +204 -0
- package/dist/vault/migrator.js.map +1 -0
- package/dist/vault/redactor.d.ts +73 -0
- package/dist/vault/redactor.d.ts.map +1 -0
- package/dist/vault/redactor.js +182 -0
- package/dist/vault/redactor.js.map +1 -0
- package/dist/vault/store.d.ts +132 -0
- package/dist/vault/store.d.ts.map +1 -0
- package/dist/vault/store.js +335 -0
- package/dist/vault/store.js.map +1 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/dist/voice/chunker.d.ts +43 -0
- package/dist/voice/chunker.d.ts.map +1 -0
- package/dist/voice/chunker.js +133 -0
- package/dist/voice/chunker.js.map +1 -0
- package/dist/voice/config.d.ts +14 -0
- package/dist/voice/config.d.ts.map +1 -0
- package/dist/voice/config.js +51 -0
- package/dist/voice/config.js.map +1 -0
- package/dist/voice/intents.d.ts +71 -0
- package/dist/voice/intents.d.ts.map +1 -0
- package/dist/voice/intents.js +0 -0
- package/dist/voice/intents.js.map +1 -0
- package/dist/voice/providers/elevenlabs.d.ts +53 -0
- package/dist/voice/providers/elevenlabs.d.ts.map +1 -0
- package/dist/voice/providers/elevenlabs.js +159 -0
- package/dist/voice/providers/elevenlabs.js.map +1 -0
- package/dist/voice/providers/google.d.ts +56 -0
- package/dist/voice/providers/google.d.ts.map +1 -0
- package/dist/voice/providers/google.js +253 -0
- package/dist/voice/providers/google.js.map +1 -0
- package/dist/voice/providers/whisper.d.ts +44 -0
- package/dist/voice/providers/whisper.d.ts.map +1 -0
- package/dist/voice/providers/whisper.js +179 -0
- package/dist/voice/providers/whisper.js.map +1 -0
- package/dist/voice/registry.d.ts +35 -0
- package/dist/voice/registry.d.ts.map +1 -0
- package/dist/voice/registry.js +48 -0
- package/dist/voice/registry.js.map +1 -0
- package/dist/voice/router.d.ts +62 -0
- package/dist/voice/router.d.ts.map +1 -0
- package/dist/voice/router.js +175 -0
- package/dist/voice/router.js.map +1 -0
- package/dist/voice/types.d.ts +116 -0
- package/dist/voice/types.d.ts.map +1 -0
- package/dist/voice/types.js +22 -0
- package/dist/voice/types.js.map +1 -0
- package/dist/voice/voiceprint/enrollment.d.ts +36 -0
- package/dist/voice/voiceprint/enrollment.d.ts.map +1 -0
- package/dist/voice/voiceprint/enrollment.js +71 -0
- package/dist/voice/voiceprint/enrollment.js.map +1 -0
- package/dist/voice/voiceprint/identify.d.ts +16 -0
- package/dist/voice/voiceprint/identify.d.ts.map +1 -0
- package/dist/voice/voiceprint/identify.js +20 -0
- package/dist/voice/voiceprint/identify.js.map +1 -0
- package/dist/voice/voiceprint/liveness.d.ts +90 -0
- package/dist/voice/voiceprint/liveness.d.ts.map +1 -0
- package/dist/voice/voiceprint/liveness.js +251 -0
- package/dist/voice/voiceprint/liveness.js.map +1 -0
- package/dist/voice/voiceprint/match.d.ts +54 -0
- package/dist/voice/voiceprint/match.d.ts.map +1 -0
- package/dist/voice/voiceprint/match.js +88 -0
- package/dist/voice/voiceprint/match.js.map +1 -0
- package/dist/voice/voiceprint/providers/local-mfcc-stub.d.ts +44 -0
- package/dist/voice/voiceprint/providers/local-mfcc-stub.d.ts.map +1 -0
- package/dist/voice/voiceprint/providers/local-mfcc-stub.js +92 -0
- package/dist/voice/voiceprint/providers/local-mfcc-stub.js.map +1 -0
- package/dist/voice/voiceprint/store.d.ts +60 -0
- package/dist/voice/voiceprint/store.d.ts.map +1 -0
- package/dist/voice/voiceprint/store.js +155 -0
- package/dist/voice/voiceprint/store.js.map +1 -0
- package/dist/voice/voiceprint/trust.d.ts +90 -0
- package/dist/voice/voiceprint/trust.d.ts.map +1 -0
- package/dist/voice/voiceprint/trust.js +150 -0
- package/dist/voice/voiceprint/trust.js.map +1 -0
- package/dist/voice/voiceprint/types.d.ts +100 -0
- package/dist/voice/voiceprint/types.d.ts.map +1 -0
- package/dist/voice/voiceprint/types.js +23 -0
- package/dist/voice/voiceprint/types.js.map +1 -0
- package/dist/voice/wake.d.ts +64 -0
- package/dist/voice/wake.d.ts.map +1 -0
- package/dist/voice/wake.js +143 -0
- package/dist/voice/wake.js.map +1 -0
- package/docs/manuals/manual.ar.html +91 -0
- package/docs/manuals/manual.de.html +100 -0
- package/docs/manuals/manual.en.html +118 -0
- package/docs/manuals/manual.es.html +120 -0
- package/docs/manuals/manual.fr.html +102 -0
- package/docs/manuals/manual.hi.html +93 -0
- package/docs/manuals/manual.it.html +93 -0
- package/docs/manuals/manual.ja.html +97 -0
- package/docs/manuals/manual.pt.html +103 -0
- package/docs/manuals/manual.zh.html +89 -0
- package/package.json +94 -0
- package/src/i18n/catalogs/ar.json +86 -0
- package/src/i18n/catalogs/de.json +86 -0
- package/src/i18n/catalogs/en.json +86 -0
- package/src/i18n/catalogs/es.json +86 -0
- package/src/i18n/catalogs/fr.json +86 -0
- package/src/i18n/catalogs/hi.json +86 -0
- package/src/i18n/catalogs/it.json +86 -0
- package/src/i18n/catalogs/ja.json +86 -0
- package/src/i18n/catalogs/pt.json +86 -0
- package/src/i18n/catalogs/zh.json +86 -0
- package/templates/react-app/README.md +43 -0
- package/templates/react-app/index.html +12 -0
- package/templates/react-app/package.json +35 -0
- package/templates/react-app/src/App.tsx +106 -0
- package/templates/react-app/src/main.tsx +21 -0
- package/templates/react-app/src/nac/manifest.ts +46 -0
- package/templates/react-app/src/styles.css +68 -0
- package/templates/react-app/tsconfig.json +19 -0
- package/templates/react-app/vite.config.ts +12 -0
- package/templates/react-app/yujin.forge.json +15 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice subsystem: type surface shared across STT, TTS, providers,
|
|
3
|
+
* and the router.
|
|
4
|
+
*
|
|
5
|
+
* Design principles:
|
|
6
|
+
* - Providers are interface-based. The router instantiates a
|
|
7
|
+
* concrete provider per request based on the active config +
|
|
8
|
+
* credentials from the vault.
|
|
9
|
+
* - No credential ever appears in the type surface or on the
|
|
10
|
+
* wire from server to browser.
|
|
11
|
+
* - Provider IDs are strings, not enums, so adding a new
|
|
12
|
+
* provider does not break existing configs that reference
|
|
13
|
+
* unknown providers (loud error at resolve time, not at
|
|
14
|
+
* parse time).
|
|
15
|
+
*
|
|
16
|
+
* ASCII-only.
|
|
17
|
+
*/
|
|
18
|
+
/** Identifiers for the providers Forge knows about today.
|
|
19
|
+
* Open string union so future providers slot in without
|
|
20
|
+
* forcing a v-bump on the config schema. */
|
|
21
|
+
export type SttProviderId = 'whisper' | 'google' | string;
|
|
22
|
+
export type TtsProviderId = 'google' | 'elevenlabs' | string;
|
|
23
|
+
/** Audio format envelopes the providers accept on input. */
|
|
24
|
+
export type SttAudioFormat = 'webm' | 'ogg' | 'wav' | 'mp3' | 'flac' | 'mp4';
|
|
25
|
+
/** Audio format the providers emit on output. */
|
|
26
|
+
export type TtsAudioFormat = 'mp3' | 'wav' | 'ogg' | 'opus';
|
|
27
|
+
export interface SttRequest {
|
|
28
|
+
/** Raw audio bytes. */
|
|
29
|
+
audio: Buffer;
|
|
30
|
+
/** Format of the audio bytes. */
|
|
31
|
+
format: SttAudioFormat;
|
|
32
|
+
/** BCP-47 language hint (e.g. "es-AR", "en-US"). When unset
|
|
33
|
+
* the provider falls back to its own auto-detect. */
|
|
34
|
+
languageHint?: string;
|
|
35
|
+
/** Override the default model for this single request. */
|
|
36
|
+
model?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface SttResponse {
|
|
39
|
+
/** Recognised text, trimmed. */
|
|
40
|
+
text: string;
|
|
41
|
+
/** Detected language (BCP-47), when the provider reports it. */
|
|
42
|
+
language?: string;
|
|
43
|
+
/** Provider-side confidence in [0, 1] when reported. */
|
|
44
|
+
confidence?: number;
|
|
45
|
+
/** Wall-clock latency of the provider call, milliseconds. */
|
|
46
|
+
latency_ms: number;
|
|
47
|
+
/** Provider id that handled the request. */
|
|
48
|
+
provider: SttProviderId;
|
|
49
|
+
/** Resolved model id. */
|
|
50
|
+
model: string;
|
|
51
|
+
}
|
|
52
|
+
export interface TtsRequest {
|
|
53
|
+
/** Plain text to synthesise. Provider must NOT execute SSML
|
|
54
|
+
* unless the caller explicitly opts in via ssml=true. */
|
|
55
|
+
text: string;
|
|
56
|
+
/** When true, the provider may interpret SSML tags in `text`. */
|
|
57
|
+
ssml?: boolean;
|
|
58
|
+
/** Voice id (provider-specific). */
|
|
59
|
+
voice?: string;
|
|
60
|
+
/** BCP-47 language hint. */
|
|
61
|
+
language?: string;
|
|
62
|
+
/** Playback speed multiplier. 1.0 = normal. 0.5..2.0 supported
|
|
63
|
+
* by every provider in scope. */
|
|
64
|
+
speed?: number;
|
|
65
|
+
/** Desired output format. */
|
|
66
|
+
format?: TtsAudioFormat;
|
|
67
|
+
}
|
|
68
|
+
export interface TtsResponse {
|
|
69
|
+
/** Raw audio bytes. */
|
|
70
|
+
audio: Buffer;
|
|
71
|
+
/** Effective output format. */
|
|
72
|
+
format: TtsAudioFormat;
|
|
73
|
+
/** Wall-clock latency, milliseconds. */
|
|
74
|
+
latency_ms: number;
|
|
75
|
+
/** Provider id that handled the request. */
|
|
76
|
+
provider: TtsProviderId;
|
|
77
|
+
/** Resolved voice id (helpful for the panel to surface what
|
|
78
|
+
* Yujin used). */
|
|
79
|
+
voice: string;
|
|
80
|
+
}
|
|
81
|
+
/** STT provider abstract surface. Implementations live under
|
|
82
|
+
* `src/voice/providers/<id>.ts`. */
|
|
83
|
+
export interface SttProvider {
|
|
84
|
+
readonly id: SttProviderId;
|
|
85
|
+
/** True when the provider has everything it needs to run
|
|
86
|
+
* (vault key present, model accessible, etc). Cheap check;
|
|
87
|
+
* the actual provider call is `transcribe`. */
|
|
88
|
+
readyToRun(): Promise<boolean>;
|
|
89
|
+
transcribe(req: SttRequest): Promise<SttResponse>;
|
|
90
|
+
}
|
|
91
|
+
/** TTS provider abstract surface. */
|
|
92
|
+
export interface TtsProvider {
|
|
93
|
+
readonly id: TtsProviderId;
|
|
94
|
+
readyToRun(): Promise<boolean>;
|
|
95
|
+
synthesize(req: TtsRequest): Promise<TtsResponse>;
|
|
96
|
+
}
|
|
97
|
+
/** User-facing voice configuration -- persisted at
|
|
98
|
+
* ~/.yujin-forge/voice-config.json. */
|
|
99
|
+
export interface VoiceConfig {
|
|
100
|
+
stt: {
|
|
101
|
+
provider: SttProviderId;
|
|
102
|
+
/** Default model id when the call does not override. */
|
|
103
|
+
model?: string;
|
|
104
|
+
/** Optional BCP-47 language hint applied to every request. */
|
|
105
|
+
language?: string;
|
|
106
|
+
};
|
|
107
|
+
tts: {
|
|
108
|
+
provider: TtsProviderId;
|
|
109
|
+
voice?: string;
|
|
110
|
+
speed?: number;
|
|
111
|
+
language?: string;
|
|
112
|
+
format?: TtsAudioFormat;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export declare const DEFAULT_VOICE_CONFIG: VoiceConfig;
|
|
116
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/voice/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice subsystem: type surface shared across STT, TTS, providers,
|
|
3
|
+
* and the router.
|
|
4
|
+
*
|
|
5
|
+
* Design principles:
|
|
6
|
+
* - Providers are interface-based. The router instantiates a
|
|
7
|
+
* concrete provider per request based on the active config +
|
|
8
|
+
* credentials from the vault.
|
|
9
|
+
* - No credential ever appears in the type surface or on the
|
|
10
|
+
* wire from server to browser.
|
|
11
|
+
* - Provider IDs are strings, not enums, so adding a new
|
|
12
|
+
* provider does not break existing configs that reference
|
|
13
|
+
* unknown providers (loud error at resolve time, not at
|
|
14
|
+
* parse time).
|
|
15
|
+
*
|
|
16
|
+
* ASCII-only.
|
|
17
|
+
*/
|
|
18
|
+
export const DEFAULT_VOICE_CONFIG = {
|
|
19
|
+
stt: { provider: 'whisper' },
|
|
20
|
+
tts: { provider: 'google' },
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/voice/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAoHH,MAAM,CAAC,MAAM,oBAAoB,GAAgB;IAC/C,GAAG,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC5B,GAAG,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;CAC5B,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voiceprint enrollment orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Takes one (or more) audio samples + a provider + a name and
|
|
5
|
+
* label, extracts the embedding(s), averages them when N > 1
|
|
6
|
+
* for robustness, and persists the result in the store.
|
|
7
|
+
*
|
|
8
|
+
* Re-enrolling with the same name overwrites silently -- the
|
|
9
|
+
* caller is expected to confirm overwrite at the UI level.
|
|
10
|
+
*
|
|
11
|
+
* Loud failures per ROADMAP sec 12.2.
|
|
12
|
+
*
|
|
13
|
+
* ASCII-only.
|
|
14
|
+
*/
|
|
15
|
+
import type { EnrolledIdentity, VoiceprintEmbedding, VoiceprintExtractRequest, VoiceprintProvider } from './types.js';
|
|
16
|
+
import { VoiceprintStore } from './store.js';
|
|
17
|
+
export interface EnrollRequest {
|
|
18
|
+
/** Stable identifier (snake_case). */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Human-readable label. */
|
|
21
|
+
label: string;
|
|
22
|
+
/** One or more audio samples for the same speaker. When > 1
|
|
23
|
+
* the embeddings are averaged + L2-renormalised. */
|
|
24
|
+
samples: VoiceprintExtractRequest[];
|
|
25
|
+
/** Optional free-form notes for the audit trail. */
|
|
26
|
+
notes?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface EnrollResult {
|
|
29
|
+
/** The persisted identity. */
|
|
30
|
+
identity: EnrolledIdentity;
|
|
31
|
+
/** Per-sample embeddings (for debugging + tests; not
|
|
32
|
+
* stored on disk). */
|
|
33
|
+
sampleEmbeddings: VoiceprintEmbedding[];
|
|
34
|
+
}
|
|
35
|
+
export declare function enroll(req: EnrollRequest, provider: VoiceprintProvider, store: VoiceprintStore): Promise<EnrollResult>;
|
|
36
|
+
//# sourceMappingURL=enrollment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrollment.d.ts","sourceRoot":"","sources":["../../../src/voice/voiceprint/enrollment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd;yDACqD;IACrD,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B;2BACuB;IACvB,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;CACzC;AAED,wBAAsB,MAAM,CAC1B,GAAG,EAAE,aAAa,EAClB,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,YAAY,CAAC,CA+DvB"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voiceprint enrollment orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Takes one (or more) audio samples + a provider + a name and
|
|
5
|
+
* label, extracts the embedding(s), averages them when N > 1
|
|
6
|
+
* for robustness, and persists the result in the store.
|
|
7
|
+
*
|
|
8
|
+
* Re-enrolling with the same name overwrites silently -- the
|
|
9
|
+
* caller is expected to confirm overwrite at the UI level.
|
|
10
|
+
*
|
|
11
|
+
* Loud failures per ROADMAP sec 12.2.
|
|
12
|
+
*
|
|
13
|
+
* ASCII-only.
|
|
14
|
+
*/
|
|
15
|
+
export async function enroll(req, provider, store) {
|
|
16
|
+
if (!req.samples || req.samples.length === 0) {
|
|
17
|
+
throw new Error('enroll: at least one audio sample required');
|
|
18
|
+
}
|
|
19
|
+
if (!(await provider.readyToRun())) {
|
|
20
|
+
throw new Error('enroll: voiceprint provider "' + provider.id + '" is not ready. '
|
|
21
|
+
+ 'Configure its credentials in the vault before enrolling.');
|
|
22
|
+
}
|
|
23
|
+
/* Extract one embedding per sample. */
|
|
24
|
+
const embeddings = [];
|
|
25
|
+
for (const sample of req.samples) {
|
|
26
|
+
const emb = await provider.extract(sample);
|
|
27
|
+
if (emb.length !== provider.embeddingSize) {
|
|
28
|
+
throw new Error('enroll: provider "' + provider.id + '" returned ' + emb.length
|
|
29
|
+
+ '-dim embedding, expected ' + provider.embeddingSize);
|
|
30
|
+
}
|
|
31
|
+
embeddings.push(emb);
|
|
32
|
+
}
|
|
33
|
+
/* Average + L2-renormalise. With N=1 this is a no-op. */
|
|
34
|
+
const dim = embeddings[0].vector.length;
|
|
35
|
+
const avg = new Array(dim).fill(0);
|
|
36
|
+
for (const emb of embeddings) {
|
|
37
|
+
const vec = emb.vector;
|
|
38
|
+
for (let i = 0; i < dim; i++) {
|
|
39
|
+
const cur = avg[i] ?? 0;
|
|
40
|
+
const add = vec[i] ?? 0;
|
|
41
|
+
avg[i] = cur + add;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (let i = 0; i < dim; i++) {
|
|
45
|
+
avg[i] = (avg[i] ?? 0) / embeddings.length;
|
|
46
|
+
}
|
|
47
|
+
let norm = 0;
|
|
48
|
+
for (const v of avg)
|
|
49
|
+
norm += (v ?? 0) * (v ?? 0);
|
|
50
|
+
norm = Math.sqrt(norm);
|
|
51
|
+
if (norm > 0) {
|
|
52
|
+
for (let i = 0; i < dim; i++) {
|
|
53
|
+
avg[i] = (avg[i] ?? 0) / norm;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const consolidated = {
|
|
57
|
+
vector: avg,
|
|
58
|
+
length: dim,
|
|
59
|
+
source: provider.id,
|
|
60
|
+
enrolled_at: new Date().toISOString(),
|
|
61
|
+
};
|
|
62
|
+
const identity = {
|
|
63
|
+
name: req.name,
|
|
64
|
+
label: req.label,
|
|
65
|
+
embedding: consolidated,
|
|
66
|
+
...(req.notes ? { notes: req.notes } : {}),
|
|
67
|
+
};
|
|
68
|
+
await store.set(identity);
|
|
69
|
+
return { identity, sampleEmbeddings: embeddings };
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=enrollment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrollment.js","sourceRoot":"","sources":["../../../src/voice/voiceprint/enrollment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-level identify(): extract an embedding from a fresh audio
|
|
3
|
+
* sample + match against the enrolled set.
|
|
4
|
+
*
|
|
5
|
+
* One call from the trust layer (V1.9). Decouples the consumer
|
|
6
|
+
* from the provider + store + matcher individually.
|
|
7
|
+
*
|
|
8
|
+
* ASCII-only.
|
|
9
|
+
*/
|
|
10
|
+
import type { VoiceprintExtractRequest, VoiceprintProvider } from './types.js';
|
|
11
|
+
import { VoiceprintStore } from './store.js';
|
|
12
|
+
import { type MatchOptions, type MatchOutput } from './match.js';
|
|
13
|
+
export interface IdentifyRequest extends VoiceprintExtractRequest {
|
|
14
|
+
}
|
|
15
|
+
export declare function identify(req: IdentifyRequest, provider: VoiceprintProvider, store: VoiceprintStore, opts?: MatchOptions): Promise<MatchOutput>;
|
|
16
|
+
//# sourceMappingURL=identify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identify.d.ts","sourceRoot":"","sources":["../../../src/voice/voiceprint/identify.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAkB,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAEjF,MAAM,WAAW,eAAgB,SAAQ,wBAAwB;CAAG;AAEpE,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,eAAe,EACtB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,WAAW,CAAC,CAUtB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-level identify(): extract an embedding from a fresh audio
|
|
3
|
+
* sample + match against the enrolled set.
|
|
4
|
+
*
|
|
5
|
+
* One call from the trust layer (V1.9). Decouples the consumer
|
|
6
|
+
* from the provider + store + matcher individually.
|
|
7
|
+
*
|
|
8
|
+
* ASCII-only.
|
|
9
|
+
*/
|
|
10
|
+
import { matchEmbedding } from './match.js';
|
|
11
|
+
export async function identify(req, provider, store, opts = {}) {
|
|
12
|
+
if (!(await provider.readyToRun())) {
|
|
13
|
+
throw new Error('identify: voiceprint provider "' + provider.id + '" is not ready. '
|
|
14
|
+
+ 'Configure credentials in the vault before running identify.');
|
|
15
|
+
}
|
|
16
|
+
const target = await provider.extract(req);
|
|
17
|
+
const enrolled = store.all();
|
|
18
|
+
return matchEmbedding(target, enrolled, opts);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=identify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identify.js","sourceRoot":"","sources":["../../../src/voice/voiceprint/identify.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,EAAE,cAAc,EAAuC,MAAM,YAAY,CAAC;AAIjF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAoB,EACpB,QAA4B,EAC5B,KAAsB,EACtB,OAAqB,EAAE;IAEvB,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,iCAAiC,GAAG,QAAQ,CAAC,EAAE,GAAG,kBAAkB;cAClE,6DAA6D,CAChE,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;IAC7B,OAAO,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Liveness check.
|
|
3
|
+
*
|
|
4
|
+
* Before an irreversible operation (npm publish, force push,
|
|
5
|
+
* delete) the chat dispatcher runs a liveness challenge:
|
|
6
|
+
*
|
|
7
|
+
* 1. generateChallenge() picks a fixed phrase + a random
|
|
8
|
+
* two-digit number. Returns the prompt text + the
|
|
9
|
+
* expected transcript.
|
|
10
|
+
* 2. The panel TTS speaks the prompt; the user repeats it.
|
|
11
|
+
* 3. verifyChallenge() runs STT on the user's audio + does
|
|
12
|
+
* a fuzzy match against the expected text. It also
|
|
13
|
+
* re-runs identify to make sure the same speaker who
|
|
14
|
+
* owned the session is still on the mic.
|
|
15
|
+
*
|
|
16
|
+
* The fuzzy match tolerates pronunciation variants + STT
|
|
17
|
+
* errors. The number-in-words component defeats trivial
|
|
18
|
+
* replay attacks: the attacker would need a recording of the
|
|
19
|
+
* user pronouncing exactly that random number.
|
|
20
|
+
*
|
|
21
|
+
* Spanish-first vocabulary; English fallback under
|
|
22
|
+
* `language: 'en'`. Adopters with other languages add a
|
|
23
|
+
* dictionary in the future.
|
|
24
|
+
*
|
|
25
|
+
* Pure -- no IO, no audio decoding. The STT + voiceprint
|
|
26
|
+
* providers are dependencies, not embedded.
|
|
27
|
+
*
|
|
28
|
+
* ASCII-only.
|
|
29
|
+
*/
|
|
30
|
+
import type { SttProvider, SttRequest } from '../types.js';
|
|
31
|
+
import type { VoiceprintProvider } from './types.js';
|
|
32
|
+
import { VoiceprintStore } from './store.js';
|
|
33
|
+
export type LivenessLanguage = 'es' | 'en';
|
|
34
|
+
export declare function numberToWords(n: number, lang: LivenessLanguage): string;
|
|
35
|
+
export interface LivenessChallenge {
|
|
36
|
+
/** The text the panel speaks via TTS + shows on screen. */
|
|
37
|
+
prompt: string;
|
|
38
|
+
/** The exact phrase the user is expected to repeat. */
|
|
39
|
+
expected: string;
|
|
40
|
+
/** Language of the challenge. */
|
|
41
|
+
language: LivenessLanguage;
|
|
42
|
+
/** Random number used in the challenge, 10-99. */
|
|
43
|
+
number: number;
|
|
44
|
+
}
|
|
45
|
+
export interface LivenessOptions {
|
|
46
|
+
language?: LivenessLanguage;
|
|
47
|
+
/** Random generator for tests. Defaults to Math.random. */
|
|
48
|
+
random?: () => number;
|
|
49
|
+
}
|
|
50
|
+
export declare function generateChallenge(opts?: LivenessOptions): LivenessChallenge;
|
|
51
|
+
/** Levenshtein distance between two strings. O(m*n) DP --
|
|
52
|
+
* fine for the short phrases the liveness check works with. */
|
|
53
|
+
export declare function levenshtein(a: string, b: string): number;
|
|
54
|
+
/** Normalise a string for fuzzy comparison: lowercase, strip
|
|
55
|
+
* punctuation, collapse whitespace. Helps STT variants
|
|
56
|
+
* ("treinta y siete" vs "treintaisiete"). */
|
|
57
|
+
export declare function normalisePhrase(s: string): string;
|
|
58
|
+
/** Fuzzy match: returns true when normalised Levenshtein
|
|
59
|
+
* distance / longer-length ratio is below 0.3 (i.e. ~70%
|
|
60
|
+
* similarity). Tolerates STT misses + accent variants. */
|
|
61
|
+
export declare function phraseMatches(transcript: string, expected: string): boolean;
|
|
62
|
+
export interface VerifyChallengeRequest {
|
|
63
|
+
/** Audio bytes the user spoke in response to the challenge. */
|
|
64
|
+
audio: Buffer;
|
|
65
|
+
/** Audio format. */
|
|
66
|
+
format: SttRequest['format'];
|
|
67
|
+
/** The challenge issued by generateChallenge. */
|
|
68
|
+
challenge: LivenessChallenge;
|
|
69
|
+
/** Identity that owned the session before the challenge.
|
|
70
|
+
* When the post-challenge identify returns a different
|
|
71
|
+
* name, the check fails even if the phrase matched. */
|
|
72
|
+
activeIdentity: string | null;
|
|
73
|
+
}
|
|
74
|
+
export interface VerifyChallengeResult {
|
|
75
|
+
/** True iff both the phrase + the speaker check passed. */
|
|
76
|
+
passed: boolean;
|
|
77
|
+
/** What the STT heard. */
|
|
78
|
+
transcript: string;
|
|
79
|
+
/** True iff phrase fuzzy-matched. */
|
|
80
|
+
phrase_match: boolean;
|
|
81
|
+
/** True iff identify confirmed the active speaker.
|
|
82
|
+
* Always true when activeIdentity is null (no enrolled
|
|
83
|
+
* user) -- in that case the check downgrades to phrase
|
|
84
|
+
* only, with a flag in the reason. */
|
|
85
|
+
identity_match: boolean;
|
|
86
|
+
/** Voice-readable reason for the dispatcher to surface. */
|
|
87
|
+
reason: string;
|
|
88
|
+
}
|
|
89
|
+
export declare function verifyChallenge(req: VerifyChallengeRequest, sttProvider: SttProvider, voiceprintProvider: VoiceprintProvider, voiceprintStore: VoiceprintStore): Promise<VerifyChallengeResult>;
|
|
90
|
+
//# sourceMappingURL=liveness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liveness.d.ts","sourceRoot":"","sources":["../../../src/voice/voiceprint/liveness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAA4B,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAAC;AAgD3C,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAiBvE;AAED,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,eAAoB,GAAG,iBAAiB,CAe/E;AAED;gEACgE;AAChE,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAsBxD;AAED;;8CAE8C;AAC9C,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED;;2DAE2D;AAC3D,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQ3E;AAED,MAAM,WAAW,sBAAsB;IACrC,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC7B,iDAAiD;IACjD,SAAS,EAAE,iBAAiB,CAAC;IAC7B;;4DAEwD;IACxD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,2DAA2D;IAC3D,MAAM,EAAE,OAAO,CAAC;IAChB,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,YAAY,EAAE,OAAO,CAAC;IACtB;;;2CAGuC;IACvC,cAAc,EAAE,OAAO,CAAC;IACxB,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,sBAAsB,EAC3B,WAAW,EAAE,WAAW,EACxB,kBAAkB,EAAE,kBAAkB,EACtC,eAAe,EAAE,eAAe,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CA2FhC"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Liveness check.
|
|
3
|
+
*
|
|
4
|
+
* Before an irreversible operation (npm publish, force push,
|
|
5
|
+
* delete) the chat dispatcher runs a liveness challenge:
|
|
6
|
+
*
|
|
7
|
+
* 1. generateChallenge() picks a fixed phrase + a random
|
|
8
|
+
* two-digit number. Returns the prompt text + the
|
|
9
|
+
* expected transcript.
|
|
10
|
+
* 2. The panel TTS speaks the prompt; the user repeats it.
|
|
11
|
+
* 3. verifyChallenge() runs STT on the user's audio + does
|
|
12
|
+
* a fuzzy match against the expected text. It also
|
|
13
|
+
* re-runs identify to make sure the same speaker who
|
|
14
|
+
* owned the session is still on the mic.
|
|
15
|
+
*
|
|
16
|
+
* The fuzzy match tolerates pronunciation variants + STT
|
|
17
|
+
* errors. The number-in-words component defeats trivial
|
|
18
|
+
* replay attacks: the attacker would need a recording of the
|
|
19
|
+
* user pronouncing exactly that random number.
|
|
20
|
+
*
|
|
21
|
+
* Spanish-first vocabulary; English fallback under
|
|
22
|
+
* `language: 'en'`. Adopters with other languages add a
|
|
23
|
+
* dictionary in the future.
|
|
24
|
+
*
|
|
25
|
+
* Pure -- no IO, no audio decoding. The STT + voiceprint
|
|
26
|
+
* providers are dependencies, not embedded.
|
|
27
|
+
*
|
|
28
|
+
* ASCII-only.
|
|
29
|
+
*/
|
|
30
|
+
import { identify } from './identify.js';
|
|
31
|
+
const PHRASES_ES = [
|
|
32
|
+
'Confirmo la operacion',
|
|
33
|
+
'Adelante con esto',
|
|
34
|
+
'Soy yo y autorizo',
|
|
35
|
+
'Por favor procede',
|
|
36
|
+
'Es correcto',
|
|
37
|
+
'Doy mi consentimiento',
|
|
38
|
+
'Estoy seguro',
|
|
39
|
+
'Sigamos adelante',
|
|
40
|
+
];
|
|
41
|
+
const PHRASES_EN = [
|
|
42
|
+
'I confirm the operation',
|
|
43
|
+
'Go ahead with this',
|
|
44
|
+
'It is me and I authorise',
|
|
45
|
+
'Please proceed',
|
|
46
|
+
'This is correct',
|
|
47
|
+
'I give my consent',
|
|
48
|
+
'I am sure',
|
|
49
|
+
'Let us continue',
|
|
50
|
+
];
|
|
51
|
+
/* "treinta y siete" / "thirty seven" -- numbers 10-99 inclusive. */
|
|
52
|
+
const NUMBER_WORDS_ES_ONES = [
|
|
53
|
+
'cero', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve',
|
|
54
|
+
];
|
|
55
|
+
const NUMBER_WORDS_ES_TEENS = [
|
|
56
|
+
'diez', 'once', 'doce', 'trece', 'catorce', 'quince',
|
|
57
|
+
'dieciseis', 'diecisiete', 'dieciocho', 'diecinueve',
|
|
58
|
+
];
|
|
59
|
+
const NUMBER_WORDS_ES_TENS = [
|
|
60
|
+
'', '', 'veinte', 'treinta', 'cuarenta', 'cincuenta',
|
|
61
|
+
'sesenta', 'setenta', 'ochenta', 'noventa',
|
|
62
|
+
];
|
|
63
|
+
const NUMBER_WORDS_EN_ONES = [
|
|
64
|
+
'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine',
|
|
65
|
+
];
|
|
66
|
+
const NUMBER_WORDS_EN_TEENS = [
|
|
67
|
+
'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
|
|
68
|
+
'sixteen', 'seventeen', 'eighteen', 'nineteen',
|
|
69
|
+
];
|
|
70
|
+
const NUMBER_WORDS_EN_TENS = [
|
|
71
|
+
'', '', 'twenty', 'thirty', 'forty', 'fifty',
|
|
72
|
+
'sixty', 'seventy', 'eighty', 'ninety',
|
|
73
|
+
];
|
|
74
|
+
export function numberToWords(n, lang) {
|
|
75
|
+
if (n < 0 || n > 99 || !Number.isInteger(n)) {
|
|
76
|
+
throw new Error('numberToWords: out of range 0..99: ' + n);
|
|
77
|
+
}
|
|
78
|
+
if (lang === 'es') {
|
|
79
|
+
if (n < 10)
|
|
80
|
+
return NUMBER_WORDS_ES_ONES[n];
|
|
81
|
+
if (n < 20)
|
|
82
|
+
return NUMBER_WORDS_ES_TEENS[n - 10];
|
|
83
|
+
if (n < 30)
|
|
84
|
+
return n === 20 ? 'veinte' : 'veinti' + NUMBER_WORDS_ES_ONES[n - 20];
|
|
85
|
+
const tens = NUMBER_WORDS_ES_TENS[Math.floor(n / 10)];
|
|
86
|
+
const ones = n % 10;
|
|
87
|
+
return ones === 0 ? tens : tens + ' y ' + NUMBER_WORDS_ES_ONES[ones];
|
|
88
|
+
}
|
|
89
|
+
if (n < 10)
|
|
90
|
+
return NUMBER_WORDS_EN_ONES[n];
|
|
91
|
+
if (n < 20)
|
|
92
|
+
return NUMBER_WORDS_EN_TEENS[n - 10];
|
|
93
|
+
const tens = NUMBER_WORDS_EN_TENS[Math.floor(n / 10)];
|
|
94
|
+
const ones = n % 10;
|
|
95
|
+
return ones === 0 ? tens : tens + ' ' + NUMBER_WORDS_EN_ONES[ones];
|
|
96
|
+
}
|
|
97
|
+
export function generateChallenge(opts = {}) {
|
|
98
|
+
const lang = opts.language ?? 'es';
|
|
99
|
+
const rng = opts.random ?? Math.random;
|
|
100
|
+
const phrases = lang === 'es' ? PHRASES_ES : PHRASES_EN;
|
|
101
|
+
const phrase = phrases[Math.floor(rng() * phrases.length)];
|
|
102
|
+
/* Pick a number 10-99 inclusive so it always has tens-place. */
|
|
103
|
+
const n = 10 + Math.floor(rng() * 90);
|
|
104
|
+
const numberWord = numberToWords(n, lang);
|
|
105
|
+
const expected = lang === 'es'
|
|
106
|
+
? phrase + ', el numero es ' + numberWord
|
|
107
|
+
: phrase + ', the number is ' + numberWord;
|
|
108
|
+
const prompt = lang === 'es'
|
|
109
|
+
? 'Para confirmar, repeti: ' + expected
|
|
110
|
+
: 'To confirm, repeat: ' + expected;
|
|
111
|
+
return { prompt, expected, language: lang, number: n };
|
|
112
|
+
}
|
|
113
|
+
/** Levenshtein distance between two strings. O(m*n) DP --
|
|
114
|
+
* fine for the short phrases the liveness check works with. */
|
|
115
|
+
export function levenshtein(a, b) {
|
|
116
|
+
if (a === b)
|
|
117
|
+
return 0;
|
|
118
|
+
if (a.length === 0)
|
|
119
|
+
return b.length;
|
|
120
|
+
if (b.length === 0)
|
|
121
|
+
return a.length;
|
|
122
|
+
const m = a.length;
|
|
123
|
+
const n = b.length;
|
|
124
|
+
let prev = new Array(n + 1);
|
|
125
|
+
let cur = new Array(n + 1);
|
|
126
|
+
for (let j = 0; j <= n; j++)
|
|
127
|
+
prev[j] = j;
|
|
128
|
+
for (let i = 1; i <= m; i++) {
|
|
129
|
+
cur[0] = i;
|
|
130
|
+
for (let j = 1; j <= n; j++) {
|
|
131
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
132
|
+
cur[j] = Math.min(cur[j - 1] + 1, /* insert */ prev[j] + 1, /* delete */ prev[j - 1] + cost);
|
|
133
|
+
}
|
|
134
|
+
[prev, cur] = [cur, prev];
|
|
135
|
+
}
|
|
136
|
+
return prev[n];
|
|
137
|
+
}
|
|
138
|
+
/** Normalise a string for fuzzy comparison: lowercase, strip
|
|
139
|
+
* punctuation, collapse whitespace. Helps STT variants
|
|
140
|
+
* ("treinta y siete" vs "treintaisiete"). */
|
|
141
|
+
export function normalisePhrase(s) {
|
|
142
|
+
return s
|
|
143
|
+
.toLowerCase()
|
|
144
|
+
.normalize('NFD')
|
|
145
|
+
.replace(/[̀-ͯ]/g, '') /* strip accents */
|
|
146
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
147
|
+
.replace(/\s+/g, ' ')
|
|
148
|
+
.trim();
|
|
149
|
+
}
|
|
150
|
+
/** Fuzzy match: returns true when normalised Levenshtein
|
|
151
|
+
* distance / longer-length ratio is below 0.3 (i.e. ~70%
|
|
152
|
+
* similarity). Tolerates STT misses + accent variants. */
|
|
153
|
+
export function phraseMatches(transcript, expected) {
|
|
154
|
+
const a = normalisePhrase(transcript);
|
|
155
|
+
const b = normalisePhrase(expected);
|
|
156
|
+
if (a === b)
|
|
157
|
+
return true;
|
|
158
|
+
if (a.length === 0 || b.length === 0)
|
|
159
|
+
return false;
|
|
160
|
+
const dist = levenshtein(a, b);
|
|
161
|
+
const longer = Math.max(a.length, b.length);
|
|
162
|
+
return (dist / longer) <= 0.3;
|
|
163
|
+
}
|
|
164
|
+
export async function verifyChallenge(req, sttProvider, voiceprintProvider, voiceprintStore) {
|
|
165
|
+
if (!req.audio || req.audio.length === 0) {
|
|
166
|
+
return {
|
|
167
|
+
passed: false,
|
|
168
|
+
transcript: '',
|
|
169
|
+
phrase_match: false,
|
|
170
|
+
identity_match: false,
|
|
171
|
+
reason: 'no audio received -- repeat the prompt',
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/* 1. STT. */
|
|
175
|
+
let transcript = '';
|
|
176
|
+
try {
|
|
177
|
+
const r = await sttProvider.transcribe({
|
|
178
|
+
audio: req.audio,
|
|
179
|
+
format: req.format,
|
|
180
|
+
languageHint: req.challenge.language === 'es' ? 'es-AR' : 'en-US',
|
|
181
|
+
});
|
|
182
|
+
transcript = r.text;
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
return {
|
|
186
|
+
passed: false,
|
|
187
|
+
transcript: '',
|
|
188
|
+
phrase_match: false,
|
|
189
|
+
identity_match: false,
|
|
190
|
+
reason: 'could not transcribe the audio: '
|
|
191
|
+
+ (err instanceof Error ? err.message : String(err)),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const phraseOk = phraseMatches(transcript, req.challenge.expected);
|
|
195
|
+
/* 2. Voiceprint check. Skip when there is no active
|
|
196
|
+
identity -- this is an unenrolled user. */
|
|
197
|
+
let identityOk = true;
|
|
198
|
+
if (req.activeIdentity !== null) {
|
|
199
|
+
try {
|
|
200
|
+
const matchResult = await identify({ audio: req.audio, format: req.format }, voiceprintProvider, voiceprintStore);
|
|
201
|
+
identityOk = matchResult.matched === req.activeIdentity;
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
return {
|
|
205
|
+
passed: false,
|
|
206
|
+
transcript,
|
|
207
|
+
phrase_match: phraseOk,
|
|
208
|
+
identity_match: false,
|
|
209
|
+
reason: 'could not run identity check: '
|
|
210
|
+
+ (err instanceof Error ? err.message : String(err)),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (!phraseOk && !identityOk) {
|
|
215
|
+
return {
|
|
216
|
+
passed: false,
|
|
217
|
+
transcript,
|
|
218
|
+
phrase_match: false,
|
|
219
|
+
identity_match: false,
|
|
220
|
+
reason: 'phrase did not match AND speaker did not match -- try again',
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
if (!phraseOk) {
|
|
224
|
+
return {
|
|
225
|
+
passed: false,
|
|
226
|
+
transcript,
|
|
227
|
+
phrase_match: false,
|
|
228
|
+
identity_match: identityOk,
|
|
229
|
+
reason: 'phrase did not match -- repeat exactly: ' + req.challenge.expected,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (!identityOk) {
|
|
233
|
+
return {
|
|
234
|
+
passed: false,
|
|
235
|
+
transcript,
|
|
236
|
+
phrase_match: true,
|
|
237
|
+
identity_match: false,
|
|
238
|
+
reason: 'phrase ok but speaker did not match the active session identity',
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
passed: true,
|
|
243
|
+
transcript,
|
|
244
|
+
phrase_match: true,
|
|
245
|
+
identity_match: true,
|
|
246
|
+
reason: req.activeIdentity
|
|
247
|
+
? 'liveness check passed -- phrase + speaker confirmed'
|
|
248
|
+
: 'liveness check passed -- phrase confirmed (no enrolled identity to cross-check)',
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=liveness.js.map
|