@seams/voiceid 0.29.0 → 0.31.0
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/README.md +2 -2
- package/dist/esm/capture/prompt.js +2 -6
- package/dist/public/sdk/voiceid/{addVoiceId-Dbd-PwWG.js → addVoiceId-8W2LeLae.js} +1 -1
- package/dist/public/sdk/voiceid/{approval-C4KlEQYu.js → approval-CvM_A5Q4.js} +1 -1
- package/dist/public/sdk/voiceid/{deviceBridge-DQLPNdC8.js → deviceBridge-CcnU-PrE.js} +1 -1
- package/dist/public/sdk/voiceid/enrollment-4Pyn_EE8.js +1 -0
- package/dist/public/sdk/voiceid/host.js +2 -2
- package/dist/public/sdk/voiceid/{prompt-vztg41jG.js → prompt-Ch0glUJT.js} +1 -1
- package/dist/public/sdk/voiceid/unlock-qE4fk0Bk.js +1 -0
- package/package.json +8 -4
- package/src/README.md +3 -2
- package/dist/public/sdk/voiceid/enrollment-DPOYb36Z.js +0 -1
- package/dist/public/sdk/voiceid/unlock-Da49yYxd.js +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @seams/voiceid
|
|
2
2
|
|
|
3
|
-
Optional browser VoiceID authentication for `@seams/wallet` 0.
|
|
3
|
+
Optional browser VoiceID authentication for `@seams/wallet` 0.31.0.
|
|
4
4
|
This package owns microphone capture, enrollment, local inference, voice UI,
|
|
5
5
|
and the model/runtime assets. The Wallet SDK owns custody, sessions, signing,
|
|
6
6
|
and the trusted host API. Existing voice enrollments keep their storage identity.
|
|
@@ -10,7 +10,7 @@ and the trusted host API. Existing voice enrollments keep their storage identity
|
|
|
10
10
|
Install matching Wallet and VoiceID versions, then import the descriptor:
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
|
-
pnpm add @seams/wallet@0.
|
|
13
|
+
pnpm add @seams/wallet@0.31.0 @seams/voiceid@0.31.0
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
```ts
|
|
@@ -409,7 +409,6 @@ var VoicePrompt = class {
|
|
|
409
409
|
return;
|
|
410
410
|
case "listening":
|
|
411
411
|
this.microphone.hidden = true;
|
|
412
|
-
delete this.status.dataset.voiceCaptureQuality;
|
|
413
412
|
this.element.dataset.capture = "listening";
|
|
414
413
|
this.record.hidden = true;
|
|
415
414
|
this.status.textContent = "Listening";
|
|
@@ -418,12 +417,9 @@ var VoicePrompt = class {
|
|
|
418
417
|
case "activity":
|
|
419
418
|
this.visual.add(event);
|
|
420
419
|
return;
|
|
421
|
-
case "measured":
|
|
422
|
-
|
|
423
|
-
this.status.dataset.voiceCaptureQuality = JSON.stringify(quality);
|
|
424
|
-
if (quality.noise === "high") this.status.textContent = "Background noise is high. Move closer to the microphone.";
|
|
420
|
+
case "measured":
|
|
421
|
+
if (event.quality.noise === "high") this.status.textContent = "Background noise is high. Move closer to the microphone.";
|
|
425
422
|
return;
|
|
426
|
-
}
|
|
427
423
|
case "processing":
|
|
428
424
|
this.element.dataset.capture = "processing";
|
|
429
425
|
this.lockLayout();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./prompt-vztg41jG.js";import{a as e,i as t,r as n,t as r}from"./deviceBridge-DQLPNdC8.js";import{a as i,d as a,i as o,o as s,s as c,t as l}from"./enrollment-DPOYb36Z.js";import{IndexedDBManager as u,WorkerDeferred as d,allocateWalletAuthMethodId as f,alphabetizeStringify as p,base64UrlDecode as m,buildMethodBoundEnvelopeOwnership as h,buildPasskeyCustodyEnvelopeRecord as g,buildVoiceIdEnvelopeFactor as _,buildWalletCustodySeedBinding as v,custodyEnvelopeBindingJsonV1 as y,fetchWithWalletHomeDiscovery as b,isVoiceIdSeedEnvelope as x,parseDigestB64u as S,parseEnvelopeRevision as C,parsePasskeyCustodyEnvelopeRecord as w,parsePasskeyEnvelopeId as T,parseResealedEnvelopeResult as E,parseVoiceIdDevicePublicKeyB64u as D,parseWalletAuthMethodId as O,parseWalletAuthMethodRecordV2 as k,parseWalletId as A,readUnlockedWalletEd25519ExportRootCapabilityV1 as j,rejectUnknownFields as M,requireRecord as N,secureRandomId as P,sha256DigestB64u as F,toError as I,walletCustodyCeremonyTransportFromWorkerContextV1 as L,walletSessionAuthorizations as R}from"../voiceid-host.js";async function z(e){let t=N(e,`pending VoiceID activation`);if(M(t,[`kind`,`walletId`,`walletAuthMethodId`,`devicePublicKeyB64u`,`deviceKeyId`,`custodyEnvelope`],`pending VoiceID activation`),t.kind!==`pending_voiceid_activation_v1`)throw Error(`Pending VoiceID activation kind is invalid.`);let n=A(t.walletId),r=O(t.walletAuthMethodId);if(!n.ok||!r.ok)throw Error(`Pending VoiceID activation identity is invalid.`);let i=D(t.devicePublicKeyB64u),a=S(t.deviceKeyId),o=w(t.custodyEnvelope);if(!x(o)||o.walletId!==n.value||o.ownership.walletAuthMethodId!==r.value||o.factor.deviceKeyId!==a||Number(o.envelopeRevision)!==1||await F(m(i))!==a)throw Error(`Pending VoiceID activation binding is invalid.`);return{kind:`pending_voiceid_activation_v1`,walletId:n.value,walletAuthMethodId:r.value,devicePublicKeyB64u:i,deviceKeyId:a,custodyEnvelope:o}}function B(e){throw Error(`Unhandled VoiceID activation plan: ${String(e)}`)}function V(e,t){let{capability:n}=e;if(n.walletId!==e.walletId||n.walletSessionId!==t.walletSessionId||n.expiresAtMs<=Date.now())throw Error(`VoiceID activation requires the unlocked owner seed and Wallet Session.`)}async function H(e,t){let n=_({deviceKeyId:t.deviceKeyId}),r=v(),i=h(e.walletAuthMethodId),a=C(1),o=y({walletId:e.walletId,envelopeId:e.envelopeId,factor:n,envelopeRevision:a,binding:r,ownership:i}),s=t.factorSecret32.slice(),c;try{c=E(await e.reseal({capability:e.capability,bindingJson:o,factorSecret:s.buffer}))}finally{s.byteLength>0&&s.fill(0)}if(await F(m(c.sealedCustodySecretB64u))!==c.ciphertextDigestB64u)throw Error(`VoiceID worker returned a mismatched ciphertext digest.`);let l=Date.now(),u=w(g({envelopeId:e.envelopeId,walletId:e.walletId,ownership:i,binding:r,factor:n,envelopeRevision:a,nonceB64u:c.nonceB64u,sealedCustodySecretB64u:c.sealedCustodySecretB64u,ciphertextDigestB64u:c.ciphertextDigestB64u,aadHashB64u:c.aadHashB64u,lifecycle:{state:`active`,activatedAtMs:l},createdAtMs:l,updatedAtMs:l}));if(!x(u))throw Error(`VoiceID worker produced an invalid seed envelope.`);return{kind:`pending_voiceid_activation_v1`,walletId:e.walletId,walletAuthMethodId:e.walletAuthMethodId,devicePublicKeyB64u:t.publicKeyB64u,deviceKeyId:t.deviceKeyId,custodyEnvelope:u}}async function U(e){let t=e.plan;switch(t.kind){case`new`:V(t,e.ownerCredential);break;case`resume`:break;default:return B(t)}let n=t.kind===`new`?t.walletId:t.pending.walletId,r=t.kind===`new`?t.walletAuthMethodId:t.pending.walletAuthMethodId,i=await e.capture({walletId:n,walletAuthMethodId:r,signal:e.signal,onCapture:e.onCapture,phrase:e.phrase}),a;try{switch(t.kind){case`new`:a=await H(t,i),await e.persistPending(a);break;case`resume`:if(t.pending.devicePublicKeyB64u!==i.publicKeyB64u||t.pending.deviceKeyId!==i.deviceKeyId)throw Error(`VoiceID activation retry found a different device key.`);a=t.pending;break;default:return B(t)}}finally{i.factorSecret32.fill(0)}if(e.signal.aborted)throw Error(`VoiceID activation was cancelled.`);let o=await e.http.activate({ownerCredential:e.ownerCredential,walletId:a.walletId,walletAuthMethodId:a.walletAuthMethodId,devicePublicKeyB64u:a.devicePublicKeyB64u,deviceKeyId:a.deviceKeyId,custodyEnvelope:a.custodyEnvelope,signal:e.signal});return await e.persistActive(o,a),{authMethod:o,pending:a}}const W=`voiceid-activation-v1:`;function G(e,t){return`${W}${encodeURIComponent(e)}:${encodeURIComponent(t)}`}function K(e){return`${W}${encodeURIComponent(e)}:`}function q(e,t){return p(e)===p(t)}async function J(e){if(e==null)return null;let t=N(e,`stored VoiceID activation`);switch(t.kind){case`pending`:return M(t,[`kind`,`pending`],`stored VoiceID activation`),{kind:`pending`,pending:await z(t.pending)};case`active`:{M(t,[`kind`,`pending`,`authMethod`],`stored VoiceID activation`);let e=await z(t.pending),n=k(t.authMethod);if(!n||n.kind!==`voiceid`||n.status!==`active`||n.walletId!==e.walletId||n.walletAuthMethodId!==e.walletAuthMethodId||n.deviceKeyId!==e.deviceKeyId||n.devicePublicKeyB64u!==e.devicePublicKeyB64u)throw Error(`Stored VoiceID activation method is invalid.`);return{kind:`active`,pending:e,authMethod:n}}default:throw Error(`Stored VoiceID activation lifecycle is invalid.`)}}var Y=class{constructor(e){this.storage=e}async persistPending(e){let t=G(e.walletId,e.walletAuthMethodId),n=await J(await this.storage.getAppState(t));if(n){if(n.kind===`pending`&&q(n.pending,e))return;throw Error(`A different VoiceID activation already owns this method ID.`)}await this.storage.setAppState(t,{kind:`pending`,pending:e})}async persistActive(e,t){let n=G(t.walletId,t.walletAuthMethodId),r=await J(await this.storage.getAppState(n));if(!r||!q(r.pending,t))throw Error(`VoiceID activation has no matching pending record.`);if(e.walletId!==t.walletId||e.walletAuthMethodId!==t.walletAuthMethodId||e.deviceKeyId!==t.deviceKeyId||e.devicePublicKeyB64u!==t.devicePublicKeyB64u)throw Error(`VoiceID server method does not match pending activation.`);await this.storage.upsertWalletAuthMethodV2(e),await this.storage.setAppState(n,{kind:`active`,pending:t,authMethod:e})}async readPending(e,t){let n=await J(await this.storage.getAppState(G(e,t)));if(!n||n.kind===`active`)return null;if(n.pending.walletId!==e||n.pending.walletAuthMethodId!==t)throw Error(`Stored VoiceID activation key does not match its record.`);return n.pending}async listPending(e){let t=await this.storage.listAppStateEntriesByPrefix(K(e)),n=[];for(let r of t){let t=await J(r.value);if(t){if(t.pending.walletId!==e||G(e,t.pending.walletAuthMethodId)!==r.key)throw Error(`Stored VoiceID activation key does not match its record.`);t.kind===`pending`&&n.push(t.pending)}}return n}};const X=`http://www.w3.org/2000/svg`;async function Z(e,t,n,r){let i=document.querySelector(`.seams-voiceid-activation-surface`);if(!(i instanceof HTMLElement))throw Error(`Open VoiceID management in the Wallet.`);let a=new Q(i,e,t,n,r);try{await a.result}finally{a.dispose()}}var Q=class{completion=new d;result=this.completion.promise;element=document.createElement(`div`);enrollment=document.createElement(`div`);enrollmentValue=document.createElement(`span`);startOver=document.createElement(`p`);status=document.createElement(`p`);actions;busy=!1;closed=!1;constructor(e,t,n,r,i){this.parent=e,this.context=t,this.scope=n,this.signal=r,this.model=i,this.element.className=`seams-voiceid-prompt seams-voiceid-management`;let a=document.createElement(`div`);a.className=`seams-voiceid-management-header`;let o=document.createElement(`button`);o.type=`button`,o.className=`seams-voiceid-management-back`,o.setAttribute(`aria-label`,`Back`),o.append($(`M15 6l-6 6 6 6`,20)),o.addEventListener(`click`,this.cancel);let s=document.createElement(`h2`);s.textContent=`Manage voice`,a.append(o,s);let c=document.createElement(`p`);c.className=`seams-voiceid-management-intro`,c.textContent=`Voice unlock on this device. Each change asks for your passkey.`,this.element.append(a,c,this.enrollmentRow()),this.actions={reset:this.resetAction(),improve:this.action(`improve`,`Improve recognition`,!0),import:this.action(`import`,`Use saved enrollment`,!1),replace:this.startOverAction()},this.status.setAttribute(`role`,`status`),this.element.append(this.status),this.enrollmentValue.textContent=`Checking…`,this.actions.import.button.hidden=!0,this.render(null,!1,!1),this.parent.replaceChildren(this.element),this.signal.addEventListener(`abort`,this.cancel,{once:!0}),this.refresh(),o.focus()}enrollmentRow(){this.enrollment.className=`seams-voiceid-management-enrollment`;let e=document.createElement(`span`);e.textContent=`Enrollment`,this.enrollmentValue.className=`seams-voiceid-management-value`;let t=document.createElement(`span`);return t.className=`seams-voiceid-management-summary`,t.append(this.enrollmentValue),this.enrollment.append(e,t),this.enrollment}resetAction(){let e=document.createElement(`button`);e.type=`button`,e.className=`seams-voiceid-management-reset`,e.textContent=`Reset`,e.setAttribute(`aria-label`,`Reset improvements`),e.addEventListener(`click`,this.begin.bind(this,`reset`));let t=document.createElement(`span`);return t.className=`seams-voiceid-management-enrollment-detail`,this.enrollment.querySelector(`.seams-voiceid-management-summary`)?.append(t),this.enrollment.append(e),{button:e,title:e,detail:t,frame:this.enrollment}}startOverAction(){return this.startOver.className=`seams-voiceid-management-section`,this.startOver.textContent=`Start over`,this.element.append(this.startOver),this.action(`replace`,`Record voice again`,!1)}action(e,t,n){let r=document.createElement(`button`);r.type=`button`,r.className=`seams-voiceid-management-action`;let i=document.createElement(`span`),a=document.createElement(`span`);a.className=`seams-voiceid-management-action-heading`;let o=document.createElement(`span`);o.id=`seams-voiceid-management-${e}-title`,o.textContent=t,a.append(o);let s=[];if(n){let t=document.createElement(`span`);t.id=`seams-voiceid-management-${e}-chip`,t.className=`seams-voiceid-management-chip`,t.textContent=`Experimental`,a.append(t),s.push(t.id)}let c=document.createElement(`span`);return c.id=`seams-voiceid-management-${e}-detail`,c.className=`seams-voiceid-management-action-detail`,s.push(c.id),i.append(a,c),r.append(i,$(`M9 6l6 6-6 6`,16)),r.setAttribute(`aria-labelledby`,o.id),r.setAttribute(`aria-describedby`,s.join(` `)),r.disabled=!0,r.addEventListener(`click`,this.begin.bind(this,e)),this.element.append(r),{button:r,title:o,detail:c,frame:r}}async refresh(){try{let e=await s(this.scope),t=e?null:await i();if(this.closed)return;this.render(e,t!==null,!0)}catch(e){this.completion.reject(I(e))}}render(e,t,n){let r=e?.adaptation??null,{improve:i,reset:a,import:o,replace:s}=this.actions;n&&(this.enrollment.dataset.enrollment=r?.kind??`missing`,this.enrollmentValue.textContent=r===null?`Not on this device`:r.kind===`adapted`?`Improved`:`Original`,i.button.hidden=e===null,o.button.hidden=!t),a.button.hidden=r?.kind!==`adapted`,a.detail.textContent=r?.kind===`adapted`?`Saved on this device`:``,i.title.textContent=r?.kind===`adapted`?`Improve again`:`Improve recognition`,i.detail.textContent=r?.kind===`adapted`?`Replace the added samples. Your original enrollment stays.`:`Add three samples. They stay until you replace or reset them.`,o.detail.textContent=`Import the voice enrollment saved in this browser.`,s.detail.textContent=r?.kind===`adapted`?`Replace your enrollment and remove improvements.`:`Replace your enrollment with three new samples.`,this.startOver.hidden=i.button.hidden&&o.button.hidden;for(let e of Object.values(this.actions))delete e.frame.dataset.state,e.button.disabled=!n;this.status.textContent=``,delete this.status.dataset.placement}show(e,t,n){let r=this.actions[e];if(this.status.textContent=n,r.button.hidden){this.status.dataset.placement=`dialog`;return}r.frame.dataset.state=t,r.detail.textContent=n}begin(e){if(!(this.busy||this.closed)){this.busy=!0;for(let e of Object.values(this.actions))e.button.disabled=!0;this.show(e,`pending`,`Confirm with your passkey to continue.`),this.perform(e)}}async perform(e){try{let r=await t(this.scope,`enrolled`),a=await s(this.scope),c=a?{kind:`present`,enrollment:a}:{kind:`missing`};if(e===`improve`&&c.kind===`missing`)throw Error(`Record or import your original voice first.`);let u=e===`import`?await i():null;if(e===`import`&&(!u||c.kind!==`missing`))throw Error(`The previous enrollment is unavailable.`);let d=await n.request(this.context,this.scope,r,c,e,this.signal);switch(this.signal.throwIfAborted(),e){case`improve`:if(c.kind!==`present`)throw Error(`Original voice enrollment is unavailable.`);await l({kind:`improve`,scope:this.scope,guard:r,snapshot:c,confirmation:d},this.signal,this.model);break;case`replace`:await l({kind:`replace`,scope:this.scope,guard:r,snapshot:c,confirmation:d},this.signal,this.model);break;case`reset`:await o(this.scope,r,c,{kind:`reset`,confirmation:d},this.signal);break;case`import`:if(!u)throw Error(`Previous voice enrollment is unavailable.`);await o(this.scope,r,c,{kind:`import`,baseline:u,confirmation:d},this.signal);break}this.closed||this.completion.resolve()}catch(t){if(this.closed)return;this.parent.replaceChildren(this.element),await this.refresh(),this.closed||this.show(e,`error`,t instanceof Error?t.message:`VoiceID could not be updated.`)}finally{this.busy=!1}}cancel=()=>{this.closed||(this.closed=!0,this.parent.dispatchEvent(new Event(`cancel`,{bubbles:!0})),this.completion.reject(Error(`VoiceID management was cancelled.`)))};dispose(){this.closed=!0,this.signal.removeEventListener(`abort`,this.cancel),this.element.remove()}};function $(e,t){let n=document.createElementNS(X,`svg`);n.setAttribute(`viewBox`,`0 0 24 24`),n.setAttribute(`width`,String(t)),n.setAttribute(`height`,String(t)),n.setAttribute(`aria-hidden`,`true`);let r=document.createElementNS(X,`path`);return r.setAttribute(`d`,e),r.setAttribute(`fill`,`none`),r.setAttribute(`stroke`,`currentColor`),r.setAttribute(`stroke-width`,`1.75`),r.setAttribute(`stroke-linecap`,`round`),r.setAttribute(`stroke-linejoin`,`round`),n.append(r),n}async function ee(e,t){return await e.requestOperation({kind:`walletCustodyCeremony`,request:{type:`resealWalletCustodyFromUnlockedCapability`,payload:{capability:t.capability,replacementEnvelopeBindingJson:t.bindingJson,replacementFactorSecret:t.factorSecret},transfer:[t.factorSecret]}})}async function te(t,n){let r=await u.resolveSelectedWalletAuthority(n);if(r.kind!==`resolved`||r.selection.lockState!==`unlocked`||r.selection.walletId!==n||r.authMethod.status!==`active`||r.authMethod.walletId!==n||r.authMethod.walletAuthorityId!==r.authority.authorityId||r.authority.state!==`active`||r.authority.walletId!==n)throw Error(`Unlock this wallet with a passkey or Email OTP before activating VoiceID.`);let i=await R.readExactWithOperationCredential({walletId:n,authorityId:r.authority.authorityId,authMethodId:r.authMethod.walletAuthMethodId});if(i.kind!==`found`||i.record.walletId!==n||i.record.authMethodId!==r.authMethod.walletAuthMethodId||i.record.authorityId!==r.authority.authorityId||i.record.expiresAtMs<=Date.now())throw Error(`VoiceID activation requires an active owner Wallet Session.`);let o=await u.listWalletAuthMethodsV2ForWallet(n);for(let e of o){if(e.kind!==`voiceid`||e.status!==`active`||e.walletAuthorityId!==r.authority.authorityId)continue;let i=await a(n,e.walletAuthMethodId);if(i&&i.deviceKeyId===e.deviceKeyId)return{kind:`enroll_existing`,walletId:n,walletAuthMethodId:e.walletAuthMethodId,context:t}}if(r.authMethod.kind===`voiceid`)throw Error(`An existing local VoiceID enrollment is required for management.`);let s=new Y(u),c=await s.listPending(n);if(c.length>1)throw Error(`More than one VoiceID activation is pending for this wallet.`);let l;if(c.length===1)l={kind:`resume`,pending:c[0]};else{let e=j(n);if(!e||e.walletSessionId!==i.operationCredential.walletSessionId)throw Error(`Unlock the wallet again to make its custody seed available for VoiceID.`);let r=T(P(`wallet-custody-envelope`,24));if(!r.ok)throw Error(`VoiceID envelope ID generation failed.`);let a=L(t.signingEngine.getSignerWorkerContext());l={kind:`new`,walletId:n,walletAuthMethodId:f(crypto.randomUUID()),envelopeId:r.value,capability:e,reseal:ee.bind(null,a)}}return{kind:`activate`,ownerCredential:i.operationCredential,plan:l,http:new e(t.configs.network.relayer.url,location.origin,b),store:s}}async function ne(e,t,n){t.throwIfAborted();let{context:r,walletId:i}=e,a=await te(r,i);return t.throwIfAborted(),a.kind===`enroll_existing`?{kind:`management`,action:re.bind(null,n,a)}:{kind:`recording`,action:ie.bind(null,n,a),expiresAtMs:null}}async function re(e,t,n){let r=await c(t.walletId,t.walletAuthMethodId);return await Z(t.context,r,n,e),n.throwIfAborted(),{kind:`activated`,walletId:t.walletId,walletAuthMethodId:t.walletAuthMethodId}}async function ie(e,t,n,i,a){let o=await U({plan:t.plan,ownerCredential:t.ownerCredential,http:t.http,signal:n,onCapture:i,phrase:a,capture:r.bind(null,e),persistPending:t.store.persistPending.bind(t.store),persistActive:t.store.persistActive.bind(t.store)});return{kind:`activated`,walletId:o.authMethod.walletId,walletAuthMethodId:o.authMethod.walletAuthMethodId}}export{ne as prepareActivationSurface};
|
|
1
|
+
import"./prompt-Ch0glUJT.js";import{a as e,i as t,r as n,t as r}from"./deviceBridge-CcnU-PrE.js";import{a as i,d as a,i as o,o as s,s as c,t as l}from"./enrollment-4Pyn_EE8.js";import{IndexedDBManager as u,WorkerDeferred as d,allocateWalletAuthMethodId as f,alphabetizeStringify as p,base64UrlDecode as m,buildMethodBoundEnvelopeOwnership as h,buildPasskeyCustodyEnvelopeRecord as g,buildVoiceIdEnvelopeFactor as _,buildWalletCustodySeedBinding as v,custodyEnvelopeBindingJsonV1 as y,fetchWithWalletHomeDiscovery as b,isVoiceIdSeedEnvelope as x,parseDigestB64u as S,parseEnvelopeRevision as C,parsePasskeyCustodyEnvelopeRecord as w,parsePasskeyEnvelopeId as T,parseResealedEnvelopeResult as E,parseVoiceIdDevicePublicKeyB64u as D,parseWalletAuthMethodId as O,parseWalletAuthMethodRecordV2 as k,parseWalletId as A,readUnlockedWalletEd25519ExportRootCapabilityV1 as j,rejectUnknownFields as M,requireRecord as N,secureRandomId as P,sha256DigestB64u as F,toError as I,walletCustodyCeremonyTransportFromWorkerContextV1 as L,walletSessionAuthorizations as R}from"../voiceid-host.js";async function z(e){let t=N(e,`pending VoiceID activation`);if(M(t,[`kind`,`walletId`,`walletAuthMethodId`,`devicePublicKeyB64u`,`deviceKeyId`,`custodyEnvelope`],`pending VoiceID activation`),t.kind!==`pending_voiceid_activation_v1`)throw Error(`Pending VoiceID activation kind is invalid.`);let n=A(t.walletId),r=O(t.walletAuthMethodId);if(!n.ok||!r.ok)throw Error(`Pending VoiceID activation identity is invalid.`);let i=D(t.devicePublicKeyB64u),a=S(t.deviceKeyId),o=w(t.custodyEnvelope);if(!x(o)||o.walletId!==n.value||o.ownership.walletAuthMethodId!==r.value||o.factor.deviceKeyId!==a||Number(o.envelopeRevision)!==1||await F(m(i))!==a)throw Error(`Pending VoiceID activation binding is invalid.`);return{kind:`pending_voiceid_activation_v1`,walletId:n.value,walletAuthMethodId:r.value,devicePublicKeyB64u:i,deviceKeyId:a,custodyEnvelope:o}}function B(e){throw Error(`Unhandled VoiceID activation plan: ${String(e)}`)}function V(e,t){let{capability:n}=e;if(n.walletId!==e.walletId||n.walletSessionId!==t.walletSessionId||n.expiresAtMs<=Date.now())throw Error(`VoiceID activation requires the unlocked owner seed and Wallet Session.`)}async function H(e,t){let n=_({deviceKeyId:t.deviceKeyId}),r=v(),i=h(e.walletAuthMethodId),a=C(1),o=y({walletId:e.walletId,envelopeId:e.envelopeId,factor:n,envelopeRevision:a,binding:r,ownership:i}),s=t.factorSecret32.slice(),c;try{c=E(await e.reseal({capability:e.capability,bindingJson:o,factorSecret:s.buffer}))}finally{s.byteLength>0&&s.fill(0)}if(await F(m(c.sealedCustodySecretB64u))!==c.ciphertextDigestB64u)throw Error(`VoiceID worker returned a mismatched ciphertext digest.`);let l=Date.now(),u=w(g({envelopeId:e.envelopeId,walletId:e.walletId,ownership:i,binding:r,factor:n,envelopeRevision:a,nonceB64u:c.nonceB64u,sealedCustodySecretB64u:c.sealedCustodySecretB64u,ciphertextDigestB64u:c.ciphertextDigestB64u,aadHashB64u:c.aadHashB64u,lifecycle:{state:`active`,activatedAtMs:l},createdAtMs:l,updatedAtMs:l}));if(!x(u))throw Error(`VoiceID worker produced an invalid seed envelope.`);return{kind:`pending_voiceid_activation_v1`,walletId:e.walletId,walletAuthMethodId:e.walletAuthMethodId,devicePublicKeyB64u:t.publicKeyB64u,deviceKeyId:t.deviceKeyId,custodyEnvelope:u}}async function U(e){let t=e.plan;switch(t.kind){case`new`:V(t,e.ownerCredential);break;case`resume`:break;default:return B(t)}let n=t.kind===`new`?t.walletId:t.pending.walletId,r=t.kind===`new`?t.walletAuthMethodId:t.pending.walletAuthMethodId,i=await e.capture({walletId:n,walletAuthMethodId:r,signal:e.signal,onCapture:e.onCapture,phrase:e.phrase}),a;try{switch(t.kind){case`new`:a=await H(t,i),await e.persistPending(a);break;case`resume`:if(t.pending.devicePublicKeyB64u!==i.publicKeyB64u||t.pending.deviceKeyId!==i.deviceKeyId)throw Error(`VoiceID activation retry found a different device key.`);a=t.pending;break;default:return B(t)}}finally{i.factorSecret32.fill(0)}if(e.signal.aborted)throw Error(`VoiceID activation was cancelled.`);let o=await e.http.activate({ownerCredential:e.ownerCredential,walletId:a.walletId,walletAuthMethodId:a.walletAuthMethodId,devicePublicKeyB64u:a.devicePublicKeyB64u,deviceKeyId:a.deviceKeyId,custodyEnvelope:a.custodyEnvelope,signal:e.signal});return await e.persistActive(o,a),{authMethod:o,pending:a}}const W=`voiceid-activation-v1:`;function G(e,t){return`${W}${encodeURIComponent(e)}:${encodeURIComponent(t)}`}function K(e){return`${W}${encodeURIComponent(e)}:`}function q(e,t){return p(e)===p(t)}async function J(e){if(e==null)return null;let t=N(e,`stored VoiceID activation`);switch(t.kind){case`pending`:return M(t,[`kind`,`pending`],`stored VoiceID activation`),{kind:`pending`,pending:await z(t.pending)};case`active`:{M(t,[`kind`,`pending`,`authMethod`],`stored VoiceID activation`);let e=await z(t.pending),n=k(t.authMethod);if(!n||n.kind!==`voiceid`||n.status!==`active`||n.walletId!==e.walletId||n.walletAuthMethodId!==e.walletAuthMethodId||n.deviceKeyId!==e.deviceKeyId||n.devicePublicKeyB64u!==e.devicePublicKeyB64u)throw Error(`Stored VoiceID activation method is invalid.`);return{kind:`active`,pending:e,authMethod:n}}default:throw Error(`Stored VoiceID activation lifecycle is invalid.`)}}var Y=class{constructor(e){this.storage=e}async persistPending(e){let t=G(e.walletId,e.walletAuthMethodId),n=await J(await this.storage.getAppState(t));if(n){if(n.kind===`pending`&&q(n.pending,e))return;throw Error(`A different VoiceID activation already owns this method ID.`)}await this.storage.setAppState(t,{kind:`pending`,pending:e})}async persistActive(e,t){let n=G(t.walletId,t.walletAuthMethodId),r=await J(await this.storage.getAppState(n));if(!r||!q(r.pending,t))throw Error(`VoiceID activation has no matching pending record.`);if(e.walletId!==t.walletId||e.walletAuthMethodId!==t.walletAuthMethodId||e.deviceKeyId!==t.deviceKeyId||e.devicePublicKeyB64u!==t.devicePublicKeyB64u)throw Error(`VoiceID server method does not match pending activation.`);await this.storage.upsertWalletAuthMethodV2(e),await this.storage.setAppState(n,{kind:`active`,pending:t,authMethod:e})}async readPending(e,t){let n=await J(await this.storage.getAppState(G(e,t)));if(!n||n.kind===`active`)return null;if(n.pending.walletId!==e||n.pending.walletAuthMethodId!==t)throw Error(`Stored VoiceID activation key does not match its record.`);return n.pending}async listPending(e){let t=await this.storage.listAppStateEntriesByPrefix(K(e)),n=[];for(let r of t){let t=await J(r.value);if(t){if(t.pending.walletId!==e||G(e,t.pending.walletAuthMethodId)!==r.key)throw Error(`Stored VoiceID activation key does not match its record.`);t.kind===`pending`&&n.push(t.pending)}}return n}};const X=`http://www.w3.org/2000/svg`;async function Z(e,t,n,r){let i=document.querySelector(`.seams-voiceid-activation-surface`);if(!(i instanceof HTMLElement))throw Error(`Open VoiceID management in the Wallet.`);let a=new Q(i,e,t,n,r);try{await a.result}finally{a.dispose()}}var Q=class{completion=new d;result=this.completion.promise;element=document.createElement(`div`);enrollment=document.createElement(`div`);enrollmentValue=document.createElement(`span`);startOver=document.createElement(`p`);status=document.createElement(`p`);actions;busy=!1;closed=!1;constructor(e,t,n,r,i){this.parent=e,this.context=t,this.scope=n,this.signal=r,this.model=i,this.element.className=`seams-voiceid-prompt seams-voiceid-management`;let a=document.createElement(`div`);a.className=`seams-voiceid-management-header`;let o=document.createElement(`button`);o.type=`button`,o.className=`seams-voiceid-management-back`,o.setAttribute(`aria-label`,`Back`),o.append($(`M15 6l-6 6 6 6`,20)),o.addEventListener(`click`,this.cancel);let s=document.createElement(`h2`);s.textContent=`Manage voice`,a.append(o,s);let c=document.createElement(`p`);c.className=`seams-voiceid-management-intro`,c.textContent=`Voice unlock on this device. Each change asks for your passkey.`,this.element.append(a,c,this.enrollmentRow()),this.actions={reset:this.resetAction(),improve:this.action(`improve`,`Improve recognition`,!0),import:this.action(`import`,`Use saved enrollment`,!1),replace:this.startOverAction()},this.status.setAttribute(`role`,`status`),this.element.append(this.status),this.enrollmentValue.textContent=`Checking…`,this.actions.import.button.hidden=!0,this.render(null,!1,!1),this.parent.replaceChildren(this.element),this.signal.addEventListener(`abort`,this.cancel,{once:!0}),this.refresh(),o.focus()}enrollmentRow(){this.enrollment.className=`seams-voiceid-management-enrollment`;let e=document.createElement(`span`);e.textContent=`Enrollment`,this.enrollmentValue.className=`seams-voiceid-management-value`;let t=document.createElement(`span`);return t.className=`seams-voiceid-management-summary`,t.append(this.enrollmentValue),this.enrollment.append(e,t),this.enrollment}resetAction(){let e=document.createElement(`button`);e.type=`button`,e.className=`seams-voiceid-management-reset`,e.textContent=`Reset`,e.setAttribute(`aria-label`,`Reset improvements`),e.addEventListener(`click`,this.begin.bind(this,`reset`));let t=document.createElement(`span`);return t.className=`seams-voiceid-management-enrollment-detail`,this.enrollment.querySelector(`.seams-voiceid-management-summary`)?.append(t),this.enrollment.append(e),{button:e,title:e,detail:t,frame:this.enrollment}}startOverAction(){return this.startOver.className=`seams-voiceid-management-section`,this.startOver.textContent=`Start over`,this.element.append(this.startOver),this.action(`replace`,`Record voice again`,!1)}action(e,t,n){let r=document.createElement(`button`);r.type=`button`,r.className=`seams-voiceid-management-action`;let i=document.createElement(`span`),a=document.createElement(`span`);a.className=`seams-voiceid-management-action-heading`;let o=document.createElement(`span`);o.id=`seams-voiceid-management-${e}-title`,o.textContent=t,a.append(o);let s=[];if(n){let t=document.createElement(`span`);t.id=`seams-voiceid-management-${e}-chip`,t.className=`seams-voiceid-management-chip`,t.textContent=`Experimental`,a.append(t),s.push(t.id)}let c=document.createElement(`span`);return c.id=`seams-voiceid-management-${e}-detail`,c.className=`seams-voiceid-management-action-detail`,s.push(c.id),i.append(a,c),r.append(i,$(`M9 6l6 6-6 6`,16)),r.setAttribute(`aria-labelledby`,o.id),r.setAttribute(`aria-describedby`,s.join(` `)),r.disabled=!0,r.addEventListener(`click`,this.begin.bind(this,e)),this.element.append(r),{button:r,title:o,detail:c,frame:r}}async refresh(){try{let e=await s(this.scope),t=e?null:await i();if(this.closed)return;this.render(e,t!==null,!0)}catch(e){this.completion.reject(I(e))}}render(e,t,n){let r=e?.adaptation??null,{improve:i,reset:a,import:o,replace:s}=this.actions;n&&(this.enrollment.dataset.enrollment=r?.kind??`missing`,this.enrollmentValue.textContent=r===null?`Not on this device`:r.kind===`adapted`?`Improved`:`Original`,i.button.hidden=e===null,o.button.hidden=!t),a.button.hidden=r?.kind!==`adapted`,a.detail.textContent=r?.kind===`adapted`?`Saved on this device`:``,i.title.textContent=r?.kind===`adapted`?`Improve again`:`Improve recognition`,i.detail.textContent=r?.kind===`adapted`?`Replace the added samples. Your original enrollment stays.`:`Add three samples. They stay until you replace or reset them.`,o.detail.textContent=`Import the voice enrollment saved in this browser.`,s.detail.textContent=r?.kind===`adapted`?`Replace your enrollment and remove improvements.`:`Replace your enrollment with three new samples.`,this.startOver.hidden=i.button.hidden&&o.button.hidden;for(let e of Object.values(this.actions))delete e.frame.dataset.state,e.button.disabled=!n;this.status.textContent=``,delete this.status.dataset.placement}show(e,t,n){let r=this.actions[e];if(this.status.textContent=n,r.button.hidden){this.status.dataset.placement=`dialog`;return}r.frame.dataset.state=t,r.detail.textContent=n}begin(e){if(!(this.busy||this.closed)){this.busy=!0;for(let e of Object.values(this.actions))e.button.disabled=!0;this.show(e,`pending`,`Confirm with your passkey to continue.`),this.perform(e)}}async perform(e){try{let r=await t(this.scope,`enrolled`),a=await s(this.scope),c=a?{kind:`present`,enrollment:a}:{kind:`missing`};if(e===`improve`&&c.kind===`missing`)throw Error(`Record or import your original voice first.`);let u=e===`import`?await i():null;if(e===`import`&&(!u||c.kind!==`missing`))throw Error(`The previous enrollment is unavailable.`);let d=await n.request(this.context,this.scope,r,c,e,this.signal);switch(this.signal.throwIfAborted(),e){case`improve`:if(c.kind!==`present`)throw Error(`Original voice enrollment is unavailable.`);await l({kind:`improve`,scope:this.scope,guard:r,snapshot:c,confirmation:d},this.signal,this.model);break;case`replace`:await l({kind:`replace`,scope:this.scope,guard:r,snapshot:c,confirmation:d},this.signal,this.model);break;case`reset`:await o(this.scope,r,c,{kind:`reset`,confirmation:d},this.signal);break;case`import`:if(!u)throw Error(`Previous voice enrollment is unavailable.`);await o(this.scope,r,c,{kind:`import`,baseline:u,confirmation:d},this.signal);break}this.closed||this.completion.resolve()}catch(t){if(this.closed)return;this.parent.replaceChildren(this.element),await this.refresh(),this.closed||this.show(e,`error`,t instanceof Error?t.message:`VoiceID could not be updated.`)}finally{this.busy=!1}}cancel=()=>{this.closed||(this.closed=!0,this.parent.dispatchEvent(new Event(`cancel`,{bubbles:!0})),this.completion.reject(Error(`VoiceID management was cancelled.`)))};dispose(){this.closed=!0,this.signal.removeEventListener(`abort`,this.cancel),this.element.remove()}};function $(e,t){let n=document.createElementNS(X,`svg`);n.setAttribute(`viewBox`,`0 0 24 24`),n.setAttribute(`width`,String(t)),n.setAttribute(`height`,String(t)),n.setAttribute(`aria-hidden`,`true`);let r=document.createElementNS(X,`path`);return r.setAttribute(`d`,e),r.setAttribute(`fill`,`none`),r.setAttribute(`stroke`,`currentColor`),r.setAttribute(`stroke-width`,`1.75`),r.setAttribute(`stroke-linecap`,`round`),r.setAttribute(`stroke-linejoin`,`round`),n.append(r),n}async function ee(e,t){return await e.requestOperation({kind:`walletCustodyCeremony`,request:{type:`resealWalletCustodyFromUnlockedCapability`,payload:{capability:t.capability,replacementEnvelopeBindingJson:t.bindingJson,replacementFactorSecret:t.factorSecret},transfer:[t.factorSecret]}})}async function te(t,n){let r=await u.resolveSelectedWalletAuthority(n);if(r.kind!==`resolved`||r.selection.lockState!==`unlocked`||r.selection.walletId!==n||r.authMethod.status!==`active`||r.authMethod.walletId!==n||r.authMethod.walletAuthorityId!==r.authority.authorityId||r.authority.state!==`active`||r.authority.walletId!==n)throw Error(`Unlock this wallet with a passkey or Email OTP before activating VoiceID.`);let i=await R.readExactWithOperationCredential({walletId:n,authorityId:r.authority.authorityId,authMethodId:r.authMethod.walletAuthMethodId});if(i.kind!==`found`||i.record.walletId!==n||i.record.authMethodId!==r.authMethod.walletAuthMethodId||i.record.authorityId!==r.authority.authorityId||i.record.expiresAtMs<=Date.now())throw Error(`VoiceID activation requires an active owner Wallet Session.`);let o=await u.listWalletAuthMethodsV2ForWallet(n);for(let e of o){if(e.kind!==`voiceid`||e.status!==`active`||e.walletAuthorityId!==r.authority.authorityId)continue;let i=await a(n,e.walletAuthMethodId);if(i&&i.deviceKeyId===e.deviceKeyId)return{kind:`enroll_existing`,walletId:n,walletAuthMethodId:e.walletAuthMethodId,context:t}}if(r.authMethod.kind===`voiceid`)throw Error(`An existing local VoiceID enrollment is required for management.`);let s=new Y(u),c=await s.listPending(n);if(c.length>1)throw Error(`More than one VoiceID activation is pending for this wallet.`);let l;if(c.length===1)l={kind:`resume`,pending:c[0]};else{let e=j(n);if(!e||e.walletSessionId!==i.operationCredential.walletSessionId)throw Error(`Unlock the wallet again to make its custody seed available for VoiceID.`);let r=T(P(`wallet-custody-envelope`,24));if(!r.ok)throw Error(`VoiceID envelope ID generation failed.`);let a=L(t.signingEngine.getSignerWorkerContext());l={kind:`new`,walletId:n,walletAuthMethodId:f(crypto.randomUUID()),envelopeId:r.value,capability:e,reseal:ee.bind(null,a)}}return{kind:`activate`,ownerCredential:i.operationCredential,plan:l,http:new e(t.configs.network.relayer.url,location.origin,b),store:s}}async function ne(e,t,n){t.throwIfAborted();let{context:r,walletId:i}=e,a=await te(r,i);return t.throwIfAborted(),a.kind===`enroll_existing`?{kind:`management`,action:re.bind(null,n,a)}:{kind:`recording`,action:ie.bind(null,n,a),expiresAtMs:null}}async function re(e,t,n){let r=await c(t.walletId,t.walletAuthMethodId);return await Z(t.context,r,n,e),n.throwIfAborted(),{kind:`activated`,walletId:t.walletId,walletAuthMethodId:t.walletAuthMethodId}}async function ie(e,t,n,i,a){let o=await U({plan:t.plan,ownerCredential:t.ownerCredential,http:t.http,signal:n,onCapture:i,phrase:a,capture:r.bind(null,e),persistPending:t.store.persistPending.bind(t.store),persistActive:t.store.persistActive.bind(t.store)});return{kind:`activated`,walletId:o.authMethod.walletId,walletAuthMethodId:o.authMethod.walletAuthMethodId}}export{ne as prepareActivationSurface};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e,d as t,l as n,m as r,t as i,y as a}from"./prompt-
|
|
1
|
+
import{c as e,d as t,l as n,m as r,t as i,y as a}from"./prompt-Ch0glUJT.js";import{f as o,n as s,s as c}from"./enrollment-4Pyn_EE8.js";import{ActionType as l,IndexedDBManager as u,TransactionReviewError as d,isPlainObject as f,parseTransactionReviewWire as p,resolveNearNetwork as m,walletForHostRequest as h,walletHostExecutionFingerprint as g}from"../voiceid-host.js";function _(e){if(e===void 0)return{kind:`random`};if(!f(e)||Object.keys(e).length!==1||typeof e.confirmationCommand!=`string`||e.confirmationCommand.length>256)throw v();let t=e.confirmationCommand.normalize(`NFC`).trim();if(!t||/[\u0000-\u001f\u007f]/.test(t))throw v();switch(/^[a-z]+\b/i.exec(t)?.[0].toLowerCase()){case`sell`:return{kind:`action`,action:`sale`};case`buy`:return{kind:`action`,action:`purchase`};case`swap`:return{kind:`action`,action:`swap`};case`send`:case`transfer`:return{kind:`action`,action:`transfer`};default:return{kind:`random`}}}function v(){return new d(`review_invalid_input`,`voiceId requires only a nonempty confirmationCommand of at most 256 characters without control characters.`)}function y(e,t){return e.attemptId===t.attemptId&&e.operationHash===t.operationHash&&e.expiresAtMs===t.expiresAtMs}async function b(r,i,o,c,l,u,d){let f=Math.min(i.expiresAtMs,Date.now()+o);if(c.aborted||f<=Date.now())throw Error(`VoiceID review expired or was cancelled.`);let p;try{p=await s({kind:`active`,scope:r},c,l,u,d)}catch(e){if(!(e instanceof n))throw e;if(c.aborted||f<=Date.now())throw Error(`VoiceID review expired or was cancelled.`);return{kind:`retry`,message:e.message}}if(c.aborted||f<=Date.now())throw Error(`VoiceID review expired or was cancelled.`);switch(p.kind){case`matched`:return{kind:`approved`};case`insufficient_audio`:return{kind:`retry`,message:e(p.quality)};case`mismatch`:return{kind:`retry`,message:new a(p.quality).message};case`phrase_mismatch`:return{kind:`retry`,message:new t().message};default:return x(p)}}function x(e){throw Error(`Unexpected voice result: ${String(e)}`)}function S(e){throw Error(`Unexpected voice outcome: ${String(e)}`)}function C(e){if(!(!f(e)&&!Array.isArray(e))){for(let t of Object.values(e))C(t);Object.freeze(e)}}async function w(e,t,n){switch(e.type){case`PM_SIGN_TX_WITH_ACTIONS`:case`PM_SIGN_DELEGATE_ACTION`:case`PM_SIGN_NEP413`:throw O();case`PM_SIGN_TEMPO`:{let r=e.payload;if(!r||r.operationKind!==`evm_transaction`)throw O();let{tx:i}=r.request,a=r.chainTarget;if(!t.config.network.chains.some(T)||a.kind!==`evm`||a.namespace!==`eip155`||a.networkSlug!==`arc-testnet`||a.chainId!==5042002||r.request.chain!==`evm`||r.request.kind!==`eip1559`||r.request.senderSignatureAlgorithm!==`secp256k1`||i.chainId!==a.chainId||typeof i.to!=`string`||!/^0x[0-9a-fA-F]{40}$/.test(i.to)||i.data!==void 0&&i.data!==`0x`||i.accessList!==void 0&&i.accessList.length!==0||i.abi!==void 0)throw O();return await D(e,t,_(r.options?.voiceId),n)}case`PM_SIGN_AND_SEND_TX`:{if(m(t.config.network.chains)!==`testnet`)throw new d(`review_unsupported_mode`,`This VoiceID prototype requires NEAR testnet.`);let r=e.payload;if(!r||r.transaction.actions.length!==1||r.transaction.actions[0].type!==l.Transfer||!(`transactionReview`in e))throw O();return await D(e,t,_(r.options.voiceId),n)}default:return{kind:`disabled`}}}function T(e){return e.network===`arc-testnet`&&e.chainId===5042002}function E(e,t){return typeof t==`bigint`?{bigint:t.toString()}:t}async function D(e,t,n,r){let i=p(e.transactionReview);if(i.validity.kind!==`expires_at`)throw new d(`review_invalid_input`,`VoiceID requires a review deadline.`);C(e);let a=new TextEncoder().encode(JSON.stringify({domain:`seams-voiceid-operation-v1`,origin:location.origin,executionFingerprint:g(t.config),request:e},E)),o=await crypto.subtle.digest(`SHA-256`,a),s=``;for(let e of new Uint8Array(o))s+=e.toString(16).padStart(2,`0`);let c=h(e,null);if(!c)throw Error(`VoiceID review requires a wallet identity.`);return{kind:`required`,approval:k.forReview(i,s,c,n,r)}}function O(){return new d(`review_unsupported_mode`,`VoiceID requires a reviewed NEAR or Arc testnet transfer.`)}var k=class e{phrase;prompt;surface;get messages(){return{confirm:`Confirm with VoiceID`,approved:`Voice approved this operation.`}}state={kind:`pending`};constructor(e,t,n,a,o){this.reviewExpiresAtMs=e,this.operationHash=t,this.walletId=n,this.confirmation=a,this.model=o,this.phrase=r(a,null),this.prompt=new i(document,`Approve transfer with VoiceID`,this.phrase,`segments`),this.prompt.record.hidden=!0,this.prompt.cancel.hidden=!0,this.surface=this.prompt.element}static forReview(t,n,r,i,a){if(t.validity.kind!==`expires_at`||!/^[0-9a-f]{64}$/.test(n))throw new d(`review_invalid_input`,`VoiceID requires a bounded operation.`);return new e(t.validity.atMs,n,r,i,a)}async prepare(e){if(this.state.kind!==`pending`)throw Error(`VoiceID preparation already started.`);this.state={kind:`preparing`},this.prompt.title.textContent=`VoiceID`,this.prompt.status.textContent=`Loading VoiceID…`;try{if(await o(this.prompt.progress,e,this.model),e.throwIfAborted(),this.state.kind!==`preparing`)throw Error(`VoiceID preparation was interrupted.`);if(Date.now()>=this.reviewExpiresAtMs)throw new d(`review_expired`,`This review has expired.`);this.prompt.progress.replaceChildren(),this.prompt.title.textContent=`Read aloud`,this.state={kind:`prepared`}}catch(e){throw this.state={kind:`closed`},e}}async approve(e){if(this.state.kind===`approved`){if(e.aborted||performance.now()>=this.state.deadline||Date.now()>=this.state.binding.expiresAtMs)throw Error(`Voice approval expired or closed.`);return{kind:`approved`}}if(this.state.kind!==`prepared`)throw Error(`Prepare VoiceID before voice approval.`);if(!navigator.userActivation.isActive||document.visibilityState!==`visible`||!document.hasFocus())throw Error(`Open the Wallet review and select Confirm to start voice capture.`);this.state={kind:`authorizing`};try{let t=await u.resolveSelectedWalletAuthority(this.walletId);if(t.kind!==`resolved`||t.authMethod.kind!==`voiceid`||t.selection.lockState!==`unlocked`)throw Error(`Unlock this wallet with VoiceID before voice approval.`);let n=await c(this.walletId,t.authMethod.walletAuthMethodId),i=this.beginAttempt(e);for(;;){let{binding:t,deadline:a,remaining:o}=i,s=await b(n,t,o,e,this.prompt.capture,this.phrase,this.model);if(this.assertListening(t),e.aborted||performance.now()>=a||Date.now()>=t.expiresAtMs)throw Error(`Voice approval expired or closed.`);switch(s.kind){case`approved`:return this.state={kind:`approved`,binding:t,deadline:a,signal:e},s;case`retry`:this.phrase=r(this.confirmation,this.phrase),this.prompt.setPhrase(this.phrase),this.prompt.record.textContent=`Try again`,this.prompt.ready(s.message),await this.waitForRetry(e),i=this.beginAttempt(e);break;default:return S(s)}}}catch(e){throw this.state={kind:`closed`},e}}beginAttempt(e){let t=Math.min(25e3,this.reviewExpiresAtMs-Date.now());if(t<250||e.aborted)throw Error(`This voice review has expired or closed.`);let n=Object.freeze({attemptId:crypto.randomUUID(),operationHash:this.operationHash,expiresAtMs:Date.now()+t}),r=performance.now()+t;return this.prompt.record.hidden=!0,this.prompt.status.textContent=`Opening microphone…`,this.state={kind:`listening`,binding:n,deadline:r,signal:e},{binding:n,deadline:r,remaining:t}}waitForRetry(e){return new Promise((t,n)=>{let r=this.prompt.record,i=i=>{clearTimeout(s),r.removeEventListener(`click`,a),e.removeEventListener(`abort`,o),i?t():n(Error(`This voice review has expired or closed.`))},a=i.bind(null,!0),o=i.bind(null,!1),s=setTimeout(o,Math.max(0,this.reviewExpiresAtMs-Date.now()));r.addEventListener(`click`,a),e.addEventListener(`abort`,o),e.aborted&&o(),r.focus()})}consume(){let e=this.state;if(this.state={kind:`consumed`},e.kind!==`approved`||e.signal.aborted||performance.now()>=e.deadline||Date.now()>=e.binding.expiresAtMs)throw Error(`A current voice approval is required before signing.`)}assertListening(e){if(this.state.kind!==`listening`||!y(e,this.state.binding))throw Error(`This voice attempt has closed.`)}};export{w as voiceIdPolicyForRequest};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d as e,i as t,o as n,s as r,y as i}from"./prompt-vztg41jG.js";import{c as a,d as o,l as s,n as c,o as l,r as u,s as d,t as f,u as p}from"./enrollment-DPOYb36Z.js";import{IndexedDBManager as m,availabilityKey as h,base64UrlDecode as g,base64UrlEncode as _,buildRelayerJsonPostRequestInit as v,buildWalletSessionAuthorizationHeaders as y,buildWebauthnP256SignatureWasm as b,createRelayerExactWalletSessionStatusPort as x,decodeCoseP256PublicKeyWasm as S,fetchWithWalletHomeDiscovery as C,isVoiceIdSeedEnvelope as w,joinNormalizedUrl as T,mpcMaterialActivationRefsEqual as E,normalizeRelayerBaseUrl as D,parseActiveWalletSessionV1 as O,parseEd25519YaoRecoveryCapabilityV1 as k,parseLinkedDeviceListResultV1 as ee,parsePasskeyCustodyEnvelopeRecord as te,parseStoredAuthMethodAvailability as ne,parseVoiceIdDeviceAssertion as re,parseVoiceIdWalletSessionChallenge as ie,parseWalletAuthMethodRecordV2 as ae,parseWalletSessionOperationCredentialV1 as oe,rejectUnknownFields as A,requireRecord as j,sha256DigestB64u as M,voiceIdWalletSessionChallengeBytes as N,walletSessionAuthorizations as P}from"../voiceid-host.js";async function F(e){let t;try{t=await e.json()}catch{throw Error(`VoiceID server returned an invalid response.`)}let n=j(t,`VoiceID server response`);if(!e.ok||n.ok!==!0)throw Error(`VoiceID server request failed (HTTP ${e.status}).`);return n}var I=class{walletOrigin;constructor(e,t,n){this.relayerUrl=e,this.send=n;let r=new URL(t);if(r.origin!==t||r.protocol!==`https:`&&r.protocol!==`http:`)throw Error(`VoiceID Wallet origin is invalid.`);this.walletOrigin=t}async activate(e){if(e.custodyEnvelope.walletId!==e.walletId||e.custodyEnvelope.ownership.walletAuthMethodId!==e.walletAuthMethodId||e.custodyEnvelope.factor.deviceKeyId!==e.deviceKeyId)throw Error(`VoiceID activation envelope does not match the device method.`);let t=new Request(T(this.relayerUrl,`/wallet/voiceid/activate`),{...v({body:{targetWalletAuthMethodId:e.walletAuthMethodId,devicePublicKeyB64u:e.devicePublicKeyB64u,custodyEnvelope:e.custodyEnvelope},headers:y(e.ownerCredential)}),signal:e.signal}),n=await F(await this.send(t));A(n,[`ok`,`authMethod`],`VoiceID activation response`);let r=ae(n.authMethod);if(!r||r.kind!==`voiceid`||r.status!==`active`||r.walletId!==e.walletId||r.walletAuthMethodId!==e.walletAuthMethodId||r.devicePublicKeyB64u!==e.devicePublicKeyB64u||r.deviceKeyId!==e.deviceKeyId)throw Error(`VoiceID server activated a different device method.`);return r}async challenge(e){let t=new Request(T(this.relayerUrl,`/wallet/voiceid/challenge`),{...v({body:{walletId:e.walletId,walletAuthMethodId:e.walletAuthMethodId}}),signal:e.signal}),n=await F(await this.send(t));A(n,[`ok`,`challenge`],`VoiceID challenge response`);let r=ie(n.challenge);if(r.walletId!==e.walletId||r.walletAuthMethodId!==e.walletAuthMethodId||r.requestOrigin!==this.walletOrigin||r.audience!==this.walletOrigin)throw Error(`VoiceID server challenge belongs to a different Wallet method or origin.`);return r}async verify(e){if(e.assertion.challenge.requestOrigin!==this.walletOrigin||e.assertion.challenge.audience!==this.walletOrigin)throw Error(`VoiceID assertion belongs to a different Wallet origin.`);let t=new Request(T(this.relayerUrl,`/wallet/voiceid/verify`),{...v({body:{assertion:e.assertion}}),signal:e.signal}),n=await F(await this.send(t));A(n,[`ok`,`walletSession`,`operationCredential`,`custodyEnvelope`,`near`],`VoiceID verification response`);let r=O(n.walletSession),i=oe(n.operationCredential),a=te(n.custodyEnvelope),o=e.assertion.challenge;if(!w(a)||r.walletId!==o.walletId||r.authMethodId!==o.walletAuthMethodId||r.authorityId!==o.walletAuthorityId||a.walletId!==o.walletId||a.ownership.walletAuthMethodId!==o.walletAuthMethodId||a.factor.deviceKeyId!==o.deviceKeyId)throw Error(`VoiceID server returned a different Wallet Session or custody envelope.`);let s=L(n.near);if(s.kind===`ready`&&(s.capability.applicationBinding.wallet_id!==r.walletId||!r.capabilitySubjects.some(R.bind(null,s.capability.materialActivation))))throw Error(`VoiceID NEAR capability belongs to another Wallet Session.`);return{walletSession:r,operationCredential:i,custodyEnvelope:a,near:s}}};function L(e){let t=j(e,`NEAR installation`);switch(t.kind){case`unavailable`:return A(t,[`kind`],`NEAR installation`),{kind:`unavailable`};case`ready`:if(A(t,[`kind`,`capability`,`remainingUses`],`NEAR installation`),typeof t.remainingUses!=`number`||!Number.isSafeInteger(t.remainingUses)||t.remainingUses<1)throw Error(`NEAR session budget is invalid.`);return{kind:`ready`,capability:k(t.capability),remainingUses:t.remainingUses};default:throw Error(`NEAR installation state is invalid.`)}}function R(e,t){return t.kind===`sign`&&t.keyFamily===`ed25519`&&E(e,t.materialActivation)}function z(e,t,n){if(t<24)return[t,1];let r=e.byteLength-n-1,i=new DataView(e.buffer,n+1),a,o=0;switch(t){case 24:r>0&&(a=i.getUint8(0),o=2);break;case 25:r>1&&(a=i.getUint16(0,!1),o=3);break;case 26:r>3&&(a=i.getUint32(0,!1),o=5);break;case 27:if(r>7){let e=i.getBigUint64(0,!1);if(e>=24n&&e<=2**53-1)return[Number(e),9]}break}if(a&&a>=24)return[a,o];throw Error(`Length not supported or not well formed`)}var B=class{constructor(e,t){Object.defineProperty(this,`tagId`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`tagValue`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.tagId=e,this.tagValue=t}get tag(){return this.tagId}get value(){return this.tagValue}};function V(e,t,n){return z(e,t,n)}function H(e,t,n){let[r,i]=V(e,t,n);return[-r-1,i]}function U(e,t,n){let[r,i]=z(e,t,n),a=n+i;return[new Uint8Array(e.buffer.slice(a,a+r)),i+r]}const W=new TextDecoder;function G(e,t,n){let[r,i]=U(e,t,n);return[W.decode(r),i]}function se(e,t,n){if(t===0)return[[],1];let[r,i]=z(e,t,n),a=i,o=[];for(let t=0;t<r;t++){if(e.byteLength-n-a<=0)throw Error(`array is not supported or well formed`);let[t,r]=q(e,n+a);o.push(t),a+=r}return[o,a]}const K=`Map is not supported or well formed`;function ce(e,t,n){if(t===0)return[new Map,1];let[r,i]=z(e,t,n),a=i,o=new Map;for(let t=0;t<r;t++){let t=e.byteLength-n-a;if(t<=0)throw Error(K);let[r,i]=q(e,n+a);if(a+=i,t-=i,t<=0||typeof r!=`string`&&typeof r!=`number`||o.has(r))throw Error(K);let[s,c]=q(e,n+a);a+=c,o.set(r,s)}return[o,a]}function le(e,t){if(t+3>e.byteLength)throw Error(`CBOR stream ended before end of Float 16`);let n=e.getUint16(t+1,!1);if(n==31744)return[1/0,3];if(n==32256)return[NaN,3];if(n==64512)return[-1/0,3];throw Error(`Float16 data is unsupported`)}function ue(e,t){if(t+5>e.byteLength)throw Error(`CBOR stream ended before end of Float 32`);return[e.getFloat32(t+1,!1),5]}function de(e,t){if(t+9>e.byteLength)throw Error(`CBOR stream ended before end of Float 64`);return[e.getFloat64(t+1,!1),9]}function fe(e,t,n){let[r,i]=z(e,t,n),[a,o]=q(e,n+i);return[new B(r,a),i+o]}function q(e,t){if(t>=e.byteLength)throw Error(`CBOR stream ended before tag value`);let n=e.getUint8(t),r=n>>5,i=n&31;switch(r){case 0:return V(e,i,t);case 1:return H(e,i,t);case 2:return U(e,i,t);case 3:return G(e,i,t);case 4:return se(e,i,t);case 5:return ce(e,i,t);case 6:return fe(e,i,t);case 7:switch(i){case 20:return[!1,1];case 21:return[!0,1];case 22:return[null,1];case 23:return[void 0,1];case 25:return le(e,t);case 26:return ue(e,t);case 27:return de(e,t)}}throw Error(`Unsupported or not well formed at ${t}`)}new TextEncoder;function pe(e,t){if(e.byteLength===0||e.byteLength<=t||t<0)throw Error(`No data`);return e instanceof Uint8Array?q(new DataView(e.buffer),t):e instanceof ArrayBuffer?q(new DataView(e),t):q(e,t)}function me(e){let[t,n]=pe(e,0);if(n!==e.byteLength)throw Error(`Data was decoded, but the whole stream was not processed ${n} != ${e.byteLength}`);return t}async function J(e,t){let n=await m.resolveSelectedWalletAuthority(e.walletId);if(n.kind!==`resolved`||n.selection.lockState!==`unlocked`||t===`activating`&&n.authMethod.kind===`voiceid`||n.authMethod.status!==`active`||n.authority.authorityId!==e.authorityId)throw Error(`Unlock this wallet with an owner method before managing VoiceID.`);let r=await P.readExactWithOperationCredential({walletId:e.walletId,authorityId:e.authorityId,authMethodId:n.authMethod.walletAuthMethodId});if(r.kind!==`found`||r.record.expiresAtMs<=Date.now())throw Error(`Unlock this wallet again before managing VoiceID.`);let i=ne(await m.getAppState(h(e.walletId,e.walletAuthMethodId))),a;if(t===`activating`)a={target:t,sessionMethodId:n.authMethod.walletAuthMethodId,walletSessionId:r.operationCredential.walletSessionId,lockGeneration:n.selection.lockGeneration,expiresAtMs:Math.min(r.record.expiresAtMs,Date.now()+5*6e4),availabilityRevision:i.revision};else{let o=(await m.listWalletAuthMethodsV2ForWallet(e.walletId)).find(Y.bind(null,e));if(!o)throw Error(`Add a passkey to this authority before changing your voice enrollment.`);a={target:t,sessionMethodId:n.authMethod.walletAuthMethodId,confirmationMethodId:o.walletAuthMethodId,walletSessionId:r.operationCredential.walletSessionId,lockGeneration:n.selection.lockGeneration,expiresAtMs:Math.min(r.record.expiresAtMs,Date.now()+5*6e4),availabilityRevision:i.revision}}return await u(e,a),a}function Y(e,t){return t.kind===`passkey`&&t.status===`active`&&t.walletId===e.walletId&&t.walletAuthorityId===e.authorityId}function he(e,t,n,r){return r.walletId===e.walletId&&r.walletAuthorityId===e.authorityId&&r.credential.kind===`passkey`&&r.credential.walletAuthMethodId===t.confirmationMethodId&&r.credential.credentialIdB64u===n}function X(e,t,n,r){return JSON.stringify([`seams-voiceid-enrollment-change-v1`,location.origin,e.walletId,e.authorityId,e.walletAuthMethodId,e.deviceKeyId,t.sessionMethodId,t.confirmationMethodId,t.walletSessionId,t.lockGeneration,t.availabilityRevision,t.expiresAtMs,r,n.kind===`present`?n.enrollment.generation:null,n.kind===`present`?n.enrollment.revision:null])}var ge=class e{consumed=!1;constructor(e,t,n){this.binding=e,this.expiresAtMs=t,this.assertCurrentOwner=n}static async request(t,n,r,i,a,o){await u(n,r);let s=_e.bind(null,t.configs.network.relayer.url,n,r);await s(o);let c=await m.getWalletAuthMethodV2(r.confirmationMethodId);if(!c||c.kind!==`passkey`||c.status!==`active`||c.walletId!==n.walletId||c.walletAuthorityId!==n.authorityId||c.rpId!==t.signingEngine.getRpId())throw Error(`The confirming passkey is unavailable for this wallet.`);let l=X(n,r,i,a),d=await M(new TextEncoder().encode(JSON.stringify({binding:l,nonce:crypto.randomUUID(),expiresAtMs:r.expiresAtMs})));o.throwIfAborted();let f=await navigator.credentials.get({publicKey:{challenge:g(d),rpId:c.rpId,allowCredentials:[{type:`public-key`,id:g(c.credentialIdB64u)}],userVerification:`required`,timeout:Math.min(6e4,r.expiresAtMs-Date.now())},signal:o});if(!(f instanceof PublicKeyCredential)||!(f.response instanceof AuthenticatorAssertionResponse)||_(new Uint8Array(f.rawId))!==c.credentialIdB64u)throw Error(`The passkey did not confirm this wallet.`);let p=f.response,h=new Uint8Array(p.clientDataJSON),v=j(JSON.parse(new TextDecoder().decode(h)),`Passkey client data`);if(v.type!==`webauthn.get`||v.challenge!==d||v.origin!==location.origin)throw Error(`The passkey confirmation belongs to another operation.`);let y=new Uint8Array(p.authenticatorData),b=new Uint8Array(await crypto.subtle.digest(`SHA-256`,new TextEncoder().encode(c.rpId)));if(y.length<37||(y[32]&5)!=5||_(y.slice(0,32))!==_(b))throw Error(`Passkey user verification is required for this Wallet origin.`);if(!await Q({cosePublicKey:g(c.credentialPublicKeyB64u),challenge:g(d),authenticatorData:y,clientBytes:h,signature:new Uint8Array(p.signature),workerCtx:t.signingEngine.getSignerWorkerContext()}))throw Error(`Passkey confirmation signature is invalid.`);return o.throwIfAborted(),await u(n,r),new e(l,r.expiresAtMs,s)}async consume(e,t,n,r,i){if(this.consumed||this.expiresAtMs<=Date.now()||this.binding!==X(e,t,n,r))throw Error(`Passkey confirmation expired or was already used. Start again.`);if(this.consumed=!0,await this.assertCurrentOwner(i),i.throwIfAborted(),this.expiresAtMs<=Date.now())throw Error(`Passkey confirmation expired. Start again.`)}};async function _e(e,t,n,r){let i=await P.readExactWithOperationCredential({walletId:t.walletId,authorityId:t.authorityId,authMethodId:n.sessionMethodId});if(i.kind!==`found`||i.operationCredential.walletSessionId!==n.walletSessionId)throw Error(`The owner session changed. Unlock this wallet and try again.`);r.throwIfAborted();let a=await x({relayerUrl:e,operationCredential:i.operationCredential,fetchImpl:Z.bind(null,r)}).read({walletSessionId:n.walletSessionId,quotaId:i.record.quotaId});if(a.status!==`active`&&a.status!==`exhausted`||a.authorization.walletId!==t.walletId||a.authorization.authorityId!==t.authorityId||a.authorization.authMethodId!==n.sessionMethodId)throw Error(`The owner session is unavailable. Unlock this wallet and try again.`);let o=await m.getWalletAuthMethodV2(n.confirmationMethodId);if(!o||o.kind!==`passkey`||!Y(t,o))throw Error(`The confirming passkey is unavailable.`);let s=new URLSearchParams({walletId:t.walletId,limit:`50`,cursor:``}),c=await Z(r,`${D(e)}/wallet/device-linking/v1/devices?${s}`,{headers:y(i.operationCredential)});if(!c.ok)throw Error(`Could not verify the confirming passkey with the wallet service.`);let{ok:l,...u}=j(await c.json(),`Wallet method inventory`);if(l!==!0)throw Error(`Could not verify the confirming passkey with the wallet service.`);if(!ee(u).ownerDevices.some(he.bind(null,t,n,o.credentialIdB64u)))throw Error(`The confirming passkey was removed or belongs to another authority.`);r.throwIfAborted()}function Z(e,t,n){return C(new Request(t,{...n,signal:AbortSignal.any([e,AbortSignal.timeout(15e3)])}))}async function Q(e){let t=me(e.cosePublicKey);if(!(t instanceof Map))throw Error(`Passkey public key is invalid.`);let n=new Uint8Array(await crypto.subtle.digest(`SHA-256`,new Uint8Array(e.clientBytes))),r=new Uint8Array(e.authenticatorData.length+n.length);if(r.set(e.authenticatorData),r.set(n,e.authenticatorData.length),t.get(1)===3&&t.get(3)===-257){let n=t.get(-1),i=t.get(-2);if(!(n instanceof Uint8Array)||!(i instanceof Uint8Array))throw Error(`RSA passkey public key is invalid.`);let a=await crypto.subtle.importKey(`jwk`,{kty:`RSA`,n:_(n),e:_(i)},{name:`RSASSA-PKCS1-v1_5`,hash:`SHA-256`},!1,[`verify`]);return crypto.subtle.verify(`RSASSA-PKCS1-v1_5`,a,new Uint8Array(e.signature),r)}if(t.get(1)!==2||t.get(3)!==-7)throw Error(`This passkey type cannot confirm VoiceID changes.`);let i=await S({cosePublicKey:e.cosePublicKey,workerCtx:e.workerCtx}),a=(await b({challenge32:e.challenge,authenticatorData:e.authenticatorData,clientDataJSON:e.clientBytes,signatureDer:e.signature,pubKeyX32:i.pubKeyX32,pubKeyY32:i.pubKeyY32,workerCtx:e.workerCtx})).slice(-128,-64),o=await crypto.subtle.importKey(`jwk`,{kty:`EC`,crv:`P-256`,x:_(i.pubKeyX32),y:_(i.pubKeyY32)},{name:`ECDSA`,namedCurve:`P-256`},!1,[`verify`]);return crypto.subtle.verify({name:`ECDSA`,hash:`SHA-256`},o,a,r)}async function ve(e,t){r();let n=await s(t.walletId,t.walletAuthMethodId),i=await m.resolveSelectedWalletAuthority(t.walletId);if(i.kind!==`resolved`)throw Error(`Select the owner wallet before enrolling VoiceID.`);let o=a(n,i.authority.authorityId);await l(o)?await $({kind:`activation`,scope:o,owner:await J(o,`activating`)},t.signal,t.onCapture,t.phrase,e):await f({kind:`activate`,scope:o,guard:await J(o,`activating`),snapshot:{kind:`missing`}},t.signal,e),t.signal.throwIfAborted();let c=await p(n);if(t.signal.aborted)throw c.fill(0),Error(`VoiceID activation was cancelled.`);return{kind:`activated`,publicKeyB64u:n.publicKeyB64u,deviceKeyId:n.deviceKeyId,factorSecret32:c}}async function ye(e,t){n();let{challenge:r}=t;if(r.requestOrigin!==location.origin||r.expiresAtMs<=Date.now())throw Error(`VoiceID challenge is expired or belongs to another Wallet origin.`);let i=await o(r.walletId,r.walletAuthMethodId);if(!i||i.deviceKeyId!==r.deviceKeyId)throw Error(`VoiceID device key is unavailable in this browser.`);if(await $({kind:`active`,scope:await d(r.walletId,r.walletAuthMethodId)},t.signal,t.onCapture,t.phrase,e),t.signal.throwIfAborted(),r.expiresAtMs<=Date.now())throw Error(`VoiceID challenge expired. Start unlock again.`);let a=await crypto.subtle.sign(`Ed25519`,i.privateKey,N(r)),s=await p(i);if(t.signal.aborted||r.expiresAtMs<=Date.now())throw s.fill(0),Error(`VoiceID unlock expired or was cancelled.`);return{kind:`unlocked`,assertion:re({kind:`voiceid_device_assertion_v1`,challenge:r,signatureB64u:_(new Uint8Array(a))}),factorSecret32:s}}async function $(n,r,a,o,s){let l=await c(n,r,a,o,s);switch(l.kind){case`matched`:return;case`insufficient_audio`:throw new t(l.quality);case`mismatch`:throw new i(l.quality);case`phrase_mismatch`:throw new e;default:return be(l)}}function be(e){throw Error(`Unexpected voice result: ${String(e)}`)}export{I as a,J as i,ye as n,ge as r,ve as t};
|
|
1
|
+
import{d as e,i as t,o as n,s as r,y as i}from"./prompt-Ch0glUJT.js";import{c as a,d as o,l as s,n as c,o as l,r as u,s as d,t as f,u as p}from"./enrollment-4Pyn_EE8.js";import{IndexedDBManager as m,availabilityKey as h,base64UrlDecode as g,base64UrlEncode as _,buildRelayerJsonPostRequestInit as v,buildWalletSessionAuthorizationHeaders as y,buildWebauthnP256SignatureWasm as b,createRelayerExactWalletSessionStatusPort as x,decodeCoseP256PublicKeyWasm as S,fetchWithWalletHomeDiscovery as C,isVoiceIdSeedEnvelope as w,joinNormalizedUrl as T,mpcMaterialActivationRefsEqual as E,normalizeRelayerBaseUrl as D,parseActiveWalletSessionV1 as O,parseEd25519YaoRecoveryCapabilityV1 as k,parseLinkedDeviceListResultV1 as ee,parsePasskeyCustodyEnvelopeRecord as te,parseStoredAuthMethodAvailability as ne,parseVoiceIdDeviceAssertion as re,parseVoiceIdWalletSessionChallenge as ie,parseWalletAuthMethodRecordV2 as ae,parseWalletSessionOperationCredentialV1 as oe,rejectUnknownFields as A,requireRecord as j,sha256DigestB64u as M,voiceIdWalletSessionChallengeBytes as N,walletSessionAuthorizations as P}from"../voiceid-host.js";async function F(e){let t;try{t=await e.json()}catch{throw Error(`VoiceID server returned an invalid response.`)}let n=j(t,`VoiceID server response`);if(!e.ok||n.ok!==!0)throw Error(`VoiceID server request failed (HTTP ${e.status}).`);return n}var I=class{walletOrigin;constructor(e,t,n){this.relayerUrl=e,this.send=n;let r=new URL(t);if(r.origin!==t||r.protocol!==`https:`&&r.protocol!==`http:`)throw Error(`VoiceID Wallet origin is invalid.`);this.walletOrigin=t}async activate(e){if(e.custodyEnvelope.walletId!==e.walletId||e.custodyEnvelope.ownership.walletAuthMethodId!==e.walletAuthMethodId||e.custodyEnvelope.factor.deviceKeyId!==e.deviceKeyId)throw Error(`VoiceID activation envelope does not match the device method.`);let t=new Request(T(this.relayerUrl,`/wallet/voiceid/activate`),{...v({body:{targetWalletAuthMethodId:e.walletAuthMethodId,devicePublicKeyB64u:e.devicePublicKeyB64u,custodyEnvelope:e.custodyEnvelope},headers:y(e.ownerCredential)}),signal:e.signal}),n=await F(await this.send(t));A(n,[`ok`,`authMethod`],`VoiceID activation response`);let r=ae(n.authMethod);if(!r||r.kind!==`voiceid`||r.status!==`active`||r.walletId!==e.walletId||r.walletAuthMethodId!==e.walletAuthMethodId||r.devicePublicKeyB64u!==e.devicePublicKeyB64u||r.deviceKeyId!==e.deviceKeyId)throw Error(`VoiceID server activated a different device method.`);return r}async challenge(e){let t=new Request(T(this.relayerUrl,`/wallet/voiceid/challenge`),{...v({body:{walletId:e.walletId,walletAuthMethodId:e.walletAuthMethodId}}),signal:e.signal}),n=await F(await this.send(t));A(n,[`ok`,`challenge`],`VoiceID challenge response`);let r=ie(n.challenge);if(r.walletId!==e.walletId||r.walletAuthMethodId!==e.walletAuthMethodId||r.requestOrigin!==this.walletOrigin||r.audience!==this.walletOrigin)throw Error(`VoiceID server challenge belongs to a different Wallet method or origin.`);return r}async verify(e){if(e.assertion.challenge.requestOrigin!==this.walletOrigin||e.assertion.challenge.audience!==this.walletOrigin)throw Error(`VoiceID assertion belongs to a different Wallet origin.`);let t=new Request(T(this.relayerUrl,`/wallet/voiceid/verify`),{...v({body:{assertion:e.assertion}}),signal:e.signal}),n=await F(await this.send(t));A(n,[`ok`,`walletSession`,`operationCredential`,`custodyEnvelope`,`near`],`VoiceID verification response`);let r=O(n.walletSession),i=oe(n.operationCredential),a=te(n.custodyEnvelope),o=e.assertion.challenge;if(!w(a)||r.walletId!==o.walletId||r.authMethodId!==o.walletAuthMethodId||r.authorityId!==o.walletAuthorityId||a.walletId!==o.walletId||a.ownership.walletAuthMethodId!==o.walletAuthMethodId||a.factor.deviceKeyId!==o.deviceKeyId)throw Error(`VoiceID server returned a different Wallet Session or custody envelope.`);let s=L(n.near);if(s.kind===`ready`&&(s.capability.applicationBinding.wallet_id!==r.walletId||!r.capabilitySubjects.some(R.bind(null,s.capability.materialActivation))))throw Error(`VoiceID NEAR capability belongs to another Wallet Session.`);return{walletSession:r,operationCredential:i,custodyEnvelope:a,near:s}}};function L(e){let t=j(e,`NEAR installation`);switch(t.kind){case`unavailable`:return A(t,[`kind`],`NEAR installation`),{kind:`unavailable`};case`ready`:if(A(t,[`kind`,`capability`,`remainingUses`],`NEAR installation`),typeof t.remainingUses!=`number`||!Number.isSafeInteger(t.remainingUses)||t.remainingUses<1)throw Error(`NEAR session budget is invalid.`);return{kind:`ready`,capability:k(t.capability),remainingUses:t.remainingUses};default:throw Error(`NEAR installation state is invalid.`)}}function R(e,t){return t.kind===`sign`&&t.keyFamily===`ed25519`&&E(e,t.materialActivation)}function z(e,t,n){if(t<24)return[t,1];let r=e.byteLength-n-1,i=new DataView(e.buffer,n+1),a,o=0;switch(t){case 24:r>0&&(a=i.getUint8(0),o=2);break;case 25:r>1&&(a=i.getUint16(0,!1),o=3);break;case 26:r>3&&(a=i.getUint32(0,!1),o=5);break;case 27:if(r>7){let e=i.getBigUint64(0,!1);if(e>=24n&&e<=2**53-1)return[Number(e),9]}break}if(a&&a>=24)return[a,o];throw Error(`Length not supported or not well formed`)}var B=class{constructor(e,t){Object.defineProperty(this,`tagId`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,`tagValue`,{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this.tagId=e,this.tagValue=t}get tag(){return this.tagId}get value(){return this.tagValue}};function V(e,t,n){return z(e,t,n)}function H(e,t,n){let[r,i]=V(e,t,n);return[-r-1,i]}function U(e,t,n){let[r,i]=z(e,t,n),a=n+i;return[new Uint8Array(e.buffer.slice(a,a+r)),i+r]}const W=new TextDecoder;function G(e,t,n){let[r,i]=U(e,t,n);return[W.decode(r),i]}function se(e,t,n){if(t===0)return[[],1];let[r,i]=z(e,t,n),a=i,o=[];for(let t=0;t<r;t++){if(e.byteLength-n-a<=0)throw Error(`array is not supported or well formed`);let[t,r]=q(e,n+a);o.push(t),a+=r}return[o,a]}const K=`Map is not supported or well formed`;function ce(e,t,n){if(t===0)return[new Map,1];let[r,i]=z(e,t,n),a=i,o=new Map;for(let t=0;t<r;t++){let t=e.byteLength-n-a;if(t<=0)throw Error(K);let[r,i]=q(e,n+a);if(a+=i,t-=i,t<=0||typeof r!=`string`&&typeof r!=`number`||o.has(r))throw Error(K);let[s,c]=q(e,n+a);a+=c,o.set(r,s)}return[o,a]}function le(e,t){if(t+3>e.byteLength)throw Error(`CBOR stream ended before end of Float 16`);let n=e.getUint16(t+1,!1);if(n==31744)return[1/0,3];if(n==32256)return[NaN,3];if(n==64512)return[-1/0,3];throw Error(`Float16 data is unsupported`)}function ue(e,t){if(t+5>e.byteLength)throw Error(`CBOR stream ended before end of Float 32`);return[e.getFloat32(t+1,!1),5]}function de(e,t){if(t+9>e.byteLength)throw Error(`CBOR stream ended before end of Float 64`);return[e.getFloat64(t+1,!1),9]}function fe(e,t,n){let[r,i]=z(e,t,n),[a,o]=q(e,n+i);return[new B(r,a),i+o]}function q(e,t){if(t>=e.byteLength)throw Error(`CBOR stream ended before tag value`);let n=e.getUint8(t),r=n>>5,i=n&31;switch(r){case 0:return V(e,i,t);case 1:return H(e,i,t);case 2:return U(e,i,t);case 3:return G(e,i,t);case 4:return se(e,i,t);case 5:return ce(e,i,t);case 6:return fe(e,i,t);case 7:switch(i){case 20:return[!1,1];case 21:return[!0,1];case 22:return[null,1];case 23:return[void 0,1];case 25:return le(e,t);case 26:return ue(e,t);case 27:return de(e,t)}}throw Error(`Unsupported or not well formed at ${t}`)}new TextEncoder;function pe(e,t){if(e.byteLength===0||e.byteLength<=t||t<0)throw Error(`No data`);return e instanceof Uint8Array?q(new DataView(e.buffer),t):e instanceof ArrayBuffer?q(new DataView(e),t):q(e,t)}function me(e){let[t,n]=pe(e,0);if(n!==e.byteLength)throw Error(`Data was decoded, but the whole stream was not processed ${n} != ${e.byteLength}`);return t}async function J(e,t){let n=await m.resolveSelectedWalletAuthority(e.walletId);if(n.kind!==`resolved`||n.selection.lockState!==`unlocked`||t===`activating`&&n.authMethod.kind===`voiceid`||n.authMethod.status!==`active`||n.authority.authorityId!==e.authorityId)throw Error(`Unlock this wallet with an owner method before managing VoiceID.`);let r=await P.readExactWithOperationCredential({walletId:e.walletId,authorityId:e.authorityId,authMethodId:n.authMethod.walletAuthMethodId});if(r.kind!==`found`||r.record.expiresAtMs<=Date.now())throw Error(`Unlock this wallet again before managing VoiceID.`);let i=ne(await m.getAppState(h(e.walletId,e.walletAuthMethodId))),a;if(t===`activating`)a={target:t,sessionMethodId:n.authMethod.walletAuthMethodId,walletSessionId:r.operationCredential.walletSessionId,lockGeneration:n.selection.lockGeneration,expiresAtMs:Math.min(r.record.expiresAtMs,Date.now()+5*6e4),availabilityRevision:i.revision};else{let o=(await m.listWalletAuthMethodsV2ForWallet(e.walletId)).find(Y.bind(null,e));if(!o)throw Error(`Add a passkey to this authority before changing your voice enrollment.`);a={target:t,sessionMethodId:n.authMethod.walletAuthMethodId,confirmationMethodId:o.walletAuthMethodId,walletSessionId:r.operationCredential.walletSessionId,lockGeneration:n.selection.lockGeneration,expiresAtMs:Math.min(r.record.expiresAtMs,Date.now()+5*6e4),availabilityRevision:i.revision}}return await u(e,a),a}function Y(e,t){return t.kind===`passkey`&&t.status===`active`&&t.walletId===e.walletId&&t.walletAuthorityId===e.authorityId}function he(e,t,n,r){return r.walletId===e.walletId&&r.walletAuthorityId===e.authorityId&&r.credential.kind===`passkey`&&r.credential.walletAuthMethodId===t.confirmationMethodId&&r.credential.credentialIdB64u===n}function X(e,t,n,r){return JSON.stringify([`seams-voiceid-enrollment-change-v1`,location.origin,e.walletId,e.authorityId,e.walletAuthMethodId,e.deviceKeyId,t.sessionMethodId,t.confirmationMethodId,t.walletSessionId,t.lockGeneration,t.availabilityRevision,t.expiresAtMs,r,n.kind===`present`?n.enrollment.generation:null,n.kind===`present`?n.enrollment.revision:null])}var ge=class e{consumed=!1;constructor(e,t,n){this.binding=e,this.expiresAtMs=t,this.assertCurrentOwner=n}static async request(t,n,r,i,a,o){await u(n,r);let s=_e.bind(null,t.configs.network.relayer.url,n,r);await s(o);let c=await m.getWalletAuthMethodV2(r.confirmationMethodId);if(!c||c.kind!==`passkey`||c.status!==`active`||c.walletId!==n.walletId||c.walletAuthorityId!==n.authorityId||c.rpId!==t.signingEngine.getRpId())throw Error(`The confirming passkey is unavailable for this wallet.`);let l=X(n,r,i,a),d=await M(new TextEncoder().encode(JSON.stringify({binding:l,nonce:crypto.randomUUID(),expiresAtMs:r.expiresAtMs})));o.throwIfAborted();let f=await navigator.credentials.get({publicKey:{challenge:g(d),rpId:c.rpId,allowCredentials:[{type:`public-key`,id:g(c.credentialIdB64u)}],userVerification:`required`,timeout:Math.min(6e4,r.expiresAtMs-Date.now())},signal:o});if(!(f instanceof PublicKeyCredential)||!(f.response instanceof AuthenticatorAssertionResponse)||_(new Uint8Array(f.rawId))!==c.credentialIdB64u)throw Error(`The passkey did not confirm this wallet.`);let p=f.response,h=new Uint8Array(p.clientDataJSON),v=j(JSON.parse(new TextDecoder().decode(h)),`Passkey client data`);if(v.type!==`webauthn.get`||v.challenge!==d||v.origin!==location.origin)throw Error(`The passkey confirmation belongs to another operation.`);let y=new Uint8Array(p.authenticatorData),b=new Uint8Array(await crypto.subtle.digest(`SHA-256`,new TextEncoder().encode(c.rpId)));if(y.length<37||(y[32]&5)!=5||_(y.slice(0,32))!==_(b))throw Error(`Passkey user verification is required for this Wallet origin.`);if(!await Q({cosePublicKey:g(c.credentialPublicKeyB64u),challenge:g(d),authenticatorData:y,clientBytes:h,signature:new Uint8Array(p.signature),workerCtx:t.signingEngine.getSignerWorkerContext()}))throw Error(`Passkey confirmation signature is invalid.`);return o.throwIfAborted(),await u(n,r),new e(l,r.expiresAtMs,s)}async consume(e,t,n,r,i){if(this.consumed||this.expiresAtMs<=Date.now()||this.binding!==X(e,t,n,r))throw Error(`Passkey confirmation expired or was already used. Start again.`);if(this.consumed=!0,await this.assertCurrentOwner(i),i.throwIfAborted(),this.expiresAtMs<=Date.now())throw Error(`Passkey confirmation expired. Start again.`)}};async function _e(e,t,n,r){let i=await P.readExactWithOperationCredential({walletId:t.walletId,authorityId:t.authorityId,authMethodId:n.sessionMethodId});if(i.kind!==`found`||i.operationCredential.walletSessionId!==n.walletSessionId)throw Error(`The owner session changed. Unlock this wallet and try again.`);r.throwIfAborted();let a=await x({relayerUrl:e,operationCredential:i.operationCredential,fetchImpl:Z.bind(null,r)}).read({walletSessionId:n.walletSessionId,quotaId:i.record.quotaId});if(a.status!==`active`&&a.status!==`exhausted`||a.authorization.walletId!==t.walletId||a.authorization.authorityId!==t.authorityId||a.authorization.authMethodId!==n.sessionMethodId)throw Error(`The owner session is unavailable. Unlock this wallet and try again.`);let o=await m.getWalletAuthMethodV2(n.confirmationMethodId);if(!o||o.kind!==`passkey`||!Y(t,o))throw Error(`The confirming passkey is unavailable.`);let s=new URLSearchParams({walletId:t.walletId,limit:`50`,cursor:``}),c=await Z(r,`${D(e)}/wallet/device-linking/v1/devices?${s}`,{headers:y(i.operationCredential)});if(!c.ok)throw Error(`Could not verify the confirming passkey with the wallet service.`);let{ok:l,...u}=j(await c.json(),`Wallet method inventory`);if(l!==!0)throw Error(`Could not verify the confirming passkey with the wallet service.`);if(!ee(u).ownerDevices.some(he.bind(null,t,n,o.credentialIdB64u)))throw Error(`The confirming passkey was removed or belongs to another authority.`);r.throwIfAborted()}function Z(e,t,n){return C(new Request(t,{...n,signal:AbortSignal.any([e,AbortSignal.timeout(15e3)])}))}async function Q(e){let t=me(e.cosePublicKey);if(!(t instanceof Map))throw Error(`Passkey public key is invalid.`);let n=new Uint8Array(await crypto.subtle.digest(`SHA-256`,new Uint8Array(e.clientBytes))),r=new Uint8Array(e.authenticatorData.length+n.length);if(r.set(e.authenticatorData),r.set(n,e.authenticatorData.length),t.get(1)===3&&t.get(3)===-257){let n=t.get(-1),i=t.get(-2);if(!(n instanceof Uint8Array)||!(i instanceof Uint8Array))throw Error(`RSA passkey public key is invalid.`);let a=await crypto.subtle.importKey(`jwk`,{kty:`RSA`,n:_(n),e:_(i)},{name:`RSASSA-PKCS1-v1_5`,hash:`SHA-256`},!1,[`verify`]);return crypto.subtle.verify(`RSASSA-PKCS1-v1_5`,a,new Uint8Array(e.signature),r)}if(t.get(1)!==2||t.get(3)!==-7)throw Error(`This passkey type cannot confirm VoiceID changes.`);let i=await S({cosePublicKey:e.cosePublicKey,workerCtx:e.workerCtx}),a=(await b({challenge32:e.challenge,authenticatorData:e.authenticatorData,clientDataJSON:e.clientBytes,signatureDer:e.signature,pubKeyX32:i.pubKeyX32,pubKeyY32:i.pubKeyY32,workerCtx:e.workerCtx})).slice(-128,-64),o=await crypto.subtle.importKey(`jwk`,{kty:`EC`,crv:`P-256`,x:_(i.pubKeyX32),y:_(i.pubKeyY32)},{name:`ECDSA`,namedCurve:`P-256`},!1,[`verify`]);return crypto.subtle.verify({name:`ECDSA`,hash:`SHA-256`},o,a,r)}async function ve(e,t){r();let n=await s(t.walletId,t.walletAuthMethodId),i=await m.resolveSelectedWalletAuthority(t.walletId);if(i.kind!==`resolved`)throw Error(`Select the owner wallet before enrolling VoiceID.`);let o=a(n,i.authority.authorityId);await l(o)?await $({kind:`activation`,scope:o,owner:await J(o,`activating`)},t.signal,t.onCapture,t.phrase,e):await f({kind:`activate`,scope:o,guard:await J(o,`activating`),snapshot:{kind:`missing`}},t.signal,e),t.signal.throwIfAborted();let c=await p(n);if(t.signal.aborted)throw c.fill(0),Error(`VoiceID activation was cancelled.`);return{kind:`activated`,publicKeyB64u:n.publicKeyB64u,deviceKeyId:n.deviceKeyId,factorSecret32:c}}async function ye(e,t){n();let{challenge:r}=t;if(r.requestOrigin!==location.origin||r.expiresAtMs<=Date.now())throw Error(`VoiceID challenge is expired or belongs to another Wallet origin.`);let i=await o(r.walletId,r.walletAuthMethodId);if(!i||i.deviceKeyId!==r.deviceKeyId)throw Error(`VoiceID device key is unavailable in this browser.`);if(await $({kind:`active`,scope:await d(r.walletId,r.walletAuthMethodId)},t.signal,t.onCapture,t.phrase,e),t.signal.throwIfAborted(),r.expiresAtMs<=Date.now())throw Error(`VoiceID challenge expired. Start unlock again.`);let a=await crypto.subtle.sign(`Ed25519`,i.privateKey,N(r)),s=await p(i);if(t.signal.aborted||r.expiresAtMs<=Date.now())throw s.fill(0),Error(`VoiceID unlock expired or was cancelled.`);return{kind:`unlocked`,assertion:re({kind:`voiceid_device_assertion_v1`,challenge:r,signatureB64u:_(new Uint8Array(a))}),factorSecret32:s}}async function $(n,r,a,o,s){let l=await c(n,r,a,o,s);switch(l.kind){case`matched`:return;case`insufficient_audio`:throw new t(l.quality);case`mismatch`:throw new i(l.quality);case`phrase_mismatch`:throw new e;default:return be(l)}}function be(e){throw Error(`Unexpected voice result: ${String(e)}`)}export{I as a,J as i,ye as n,ge as r,ve as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,p as s,s as c,t as l,u as ee,v as te}from"./prompt-Ch0glUJT.js";import{IndexedDBManager as u,SEAMS_WALLET_STORES as d,WorkerDeferred as f,availabilityKey as p,base64UrlDecode as m,base64UrlEncode as ne,buildDelegatedWalletAuthorityV1 as h,buildFullOwnerPermissionsV1 as re,isFiniteNumber as ie,isPlainObject as g,parseStoredAuthMethodAvailability as ae,parseVoiceIdDevicePublicKeyB64u as _,parseWalletAuthMethodV2StorageRow as v,parseWalletAuthorityStorageRow as oe,parseWalletSelectionStorageRow as y,requireRecord as b,sameDelegatedWalletAuthorityV1 as se,sha256DigestB64u as x,toError as S}from"../voiceid-host.js";const C=`e04dd7a6a1e23fc7b89e47682ea9d0a3946cf7bc59d07922c3db3efc52ae4269`,w=`redimnet-b1:${C}`,T=new a({path:`/sdk/voiceid/redimnet-b1.onnx`,sha256:C,bytes:8859593,label:`Speaker verification`});async function E(e){let t=await T.prepare(),n=new(await(import(`./ort.wasm.bundle.min-7y-hlRN8.js`))).Tensor(`float32`,e,[1,e.length]);try{let e=await t.run({samples:n});try{let t=e.embedding;if(!t||!(t.data instanceof Float32Array))throw Error(`VoiceID model returned an invalid embedding.`);return D(Array.from(t.data))}finally{for(let t of Object.values(e))t.dispose()}}finally{n.dispose()}}function D(e){if(e.length!==192)throw Error(`VoiceID template dimensions are invalid.`);let t=0;for(let n of e){if(!Number.isFinite(n))throw Error(`VoiceID template is invalid.`);t+=n*n}if(t<1e-12)throw Error(`VoiceID template contains no signal.`);let n=Math.sqrt(t),r=[];for(let t of e)r.push(t/n);return r}function O(e,t){if(e.length!==192||t.length!==192)throw Error(`VoiceID templates are incompatible.`);let n=0;for(let r=0;r<e.length;r+=1)n+=e[r]*t[r];return n}function k(e){return e.asset}function A(e,t){return e+t.size}var j=class{element=document.createElement(`section`);status=document.createElement(`p`);meter=document.createElement(`progress`);retryButton=document.createElement(`button`);retry=null;cancelled=new f;reveal;received=new Map;models;assets;total;constructor(e,t,n){this.parent=e,this.signal=t,this.models=[T,s(n),ee],this.assets=[te,...this.models.map(k)],this.total=this.assets.reduce(A,0),this.element.className=`seams-voiceid-model-download`,this.status.setAttribute(`role`,`status`),this.status.setAttribute(`aria-live`,`polite`),this.meter.setAttribute(`aria-label`,`VoiceID download`),this.meter.style.width=`100%`,this.retryButton.type=`button`,this.retryButton.className=`seams-voiceid-primary`,this.retryButton.textContent=`Retry loading`,this.retryButton.hidden=!0,this.retryButton.addEventListener(`click`,this.retryLoading),this.element.append(this.status,this.meter,this.retryButton),this.status.textContent=`Loading saved VoiceID…`,this.reveal=setTimeout(e.append.bind(e,this.element),300),t.addEventListener(`abort`,this.abort,{once:!0})}settle=()=>{clearTimeout(this.reveal),this.signal.removeEventListener(`abort`,this.abort),this.retryButton.removeEventListener(`click`,this.retryLoading)};abort=()=>{let e=new DOMException(`VoiceID preparation was cancelled.`,`AbortError`);this.cancelled.reject(e),this.retry?.reject(e)};retryLoading=()=>{this.retry?.resolve(),this.retry=null};async prepare(){this.signal.throwIfAborted(),await Promise.race([this.prepareUntilReady(),this.cancelled.promise])}async prepareUntilReady(){for(;;){this.signal.throwIfAborted(),this.retryButton.hidden=!0,this.meter.hidden=!1;try{for(let e of this.assets)await e.download(this.update.bind(this,e)),this.signal.throwIfAborted();this.preparing();for(let e of this.models)await e.prepare(),this.signal.throwIfAborted();this.ready();return}catch(t){if(this.signal.throwIfAborted(),!(t instanceof e))throw t;clearTimeout(this.reveal),this.parent.append(this.element),this.element.dataset.state=`failed`,this.meter.hidden=!0,this.status.textContent=t.message,this.retry=new f,this.retryButton.hidden=!1,this.retryButton.focus(),await this.retry.promise}}}update=(e,t)=>{switch(t.kind){case`checking_cache`:return;case`downloading`:this.received.set(e,t.received),this.element.dataset.state=`downloading`;break;case`ready`:this.received.set(e,e.size);break;default:}if(this.element.dataset.state!==`downloading`)return;let n=0;for(let e of this.received.values())n+=e;this.meter.max=this.total,this.meter.value=n,this.status.textContent=`Downloading VoiceID: ${(n/1e6).toFixed(1)} of ${(this.total/1e6).toFixed(1)} MB · ${Math.floor(n/this.total*100)}%`};preparing(){this.element.dataset.state=`preparing`,this.meter.removeAttribute(`value`),this.status.textContent=`Preparing VoiceID…`}ready(){this.element.dataset.state=`ready`,this.meter.hidden=!0,this.status.textContent=`VoiceID is ready.`}};async function M(e,t,n){let r=new j(e,t,n);try{await r.prepare()}finally{r.settle()}}function N(e,t){return`voiceid-browser-device-v1:${encodeURIComponent(e)}:${encodeURIComponent(t)}`}function P(e,t,n){return new TextEncoder().encode(JSON.stringify([`seams-voiceid-browser-factor-v1`,location.origin,e,t,n]))}async function F(e,t){let n=await u.getAppState(N(e,t));if(n===void 0)return null;let r=b(n,`VoiceID device record`);if(r.kind!==`browser_voiceid_device_v1`||r.walletId!==e||r.walletAuthMethodId!==t)throw Error(`VoiceID device belongs to another wallet method.`);let{privateKey:i,wrappingKey:a,nonce:o,ciphertext:s}=r;if(!(i instanceof CryptoKey)||i.type!==`private`||i.extractable||i.algorithm.name!==`Ed25519`||!i.usages.includes(`sign`)||!(a instanceof CryptoKey)||a.type!==`secret`||a.extractable||a.algorithm.name!==`AES-GCM`||!a.usages.includes(`decrypt`)||!(o instanceof Uint8Array)||o.length!==12||!(s instanceof ArrayBuffer)||s.byteLength!==48)throw Error(`VoiceID device storage is invalid.`);let c=_(r.publicKeyB64u),l=await x(m(c));if(r.deviceKeyId!==l)throw Error(`VoiceID device key binding is invalid.`);return{kind:`browser_voiceid_device_v1`,walletId:e,walletAuthMethodId:t,privateKey:i,wrappingKey:a,nonce:new Uint8Array(o),ciphertext:s,publicKeyB64u:c,deviceKeyId:l}}async function ce(e,t){return await navigator.locks.request(N(e,t),I.bind(null,e,t))}async function I(e,t){let n=await F(e,t);if(n)return n;let r=await crypto.subtle.generateKey({name:`Ed25519`},!1,[`sign`,`verify`]),i=_(ne(new Uint8Array(await crypto.subtle.exportKey(`raw`,r.publicKey)))),a=await x(m(i)),o=await crypto.subtle.generateKey({name:`AES-GCM`,length:256},!1,[`encrypt`,`decrypt`]),s=crypto.getRandomValues(new Uint8Array(32)),c=crypto.getRandomValues(new Uint8Array(12));try{let n=await crypto.subtle.encrypt({name:`AES-GCM`,iv:c,additionalData:P(e,t,a)},o,s),l={kind:`browser_voiceid_device_v1`,walletId:e,walletAuthMethodId:t,privateKey:r.privateKey,publicKeyB64u:i,deviceKeyId:a,wrappingKey:o,nonce:c,ciphertext:n};return await u.setAppState(N(e,t),l),l}finally{s.fill(0)}}async function L(e){let t=await crypto.subtle.decrypt({name:`AES-GCM`,iv:e.nonce,additionalData:P(e.walletId,e.walletAuthMethodId,e.deviceKeyId)},e.wrappingKey,e.ciphertext);return new Uint8Array(t)}const R=`voiceid-browser-enrollment-v1`,z=.2,B=[d.appState,d.walletSelections,d.walletAuthMethods,d.walletAuthorities];function V(e,t){return{walletId:e.walletId,walletAuthMethodId:e.walletAuthMethodId,deviceKeyId:e.deviceKeyId,authorityId:t}}async function H(e,t){let n=await u.getWalletAuthMethodV2(t),r=await F(e,t);if(!n||n.kind!==`voiceid`||n.status!==`active`||n.walletId!==e||!r||n.deviceKeyId!==r.deviceKeyId)throw Error(`VoiceID is unavailable for this wallet method.`);return V(r,n.walletAuthorityId)}function U(e){return`voiceid-browser-enrollment-v2:${[e.walletId,e.authorityId,e.walletAuthMethodId,e.deviceKeyId].map(encodeURIComponent).join(`:`)}`}function W(e){if(!Array.isArray(e)||!e.every(ie))throw Error(`VoiceID template is invalid.`);let t=D(e);for(let n=0;n<e.length;n+=1)if(Math.abs(e[n]-t[n])>1e-6)throw Error(`VoiceID template is not normalized.`);return e.slice()}function G(e,t){if(e===void 0)return null;let n=b(e,`VoiceID enrollment`),r=b(n.scope,`VoiceID enrollment scope`);if(n.kind!==`browser_voiceid_enrollment_v2`||r.walletId!==t.walletId||r.authorityId!==t.authorityId||r.walletAuthMethodId!==t.walletAuthMethodId||r.deviceKeyId!==t.deviceKeyId||typeof n.generation!=`string`||!n.generation||typeof n.revision!=`number`||!Number.isSafeInteger(n.revision)||n.revision<0)throw Error(`VoiceID enrollment identity is invalid.`);if(n.model!==w)return null;let i=W(n.baseline),a={kind:`baseline`};try{let e=b(n.adaptation,`VoiceID improvements`);if(e.kind===`adapted`&&Array.isArray(e.samples)&&e.samples.length===3){let t=e.samples.map(W);q(i,t),a={kind:`adapted`,samples:t}}}catch{a={kind:`baseline`}}return{kind:`browser_voiceid_enrollment_v2`,scope:t,model:w,generation:n.generation,revision:n.revision,baseline:i,adaptation:a}}function K(e,t){let n=Array(192).fill(0);for(let e of t)for(let r=0;r<n.length;r+=1)n[r]+=e[r]/t.length;let r=D(n);for(let t=0;t<n.length;t+=1)n[t]=(1-z)*e[t]+z*r[t];return D(n)}function q(e,t){if(t.length!==3)throw Error(`Record three samples before saving voice improvements.`);for(let n of t)if(O(e,n)<.45)throw Error(`This sample differs from your original voice. Try again or re-record your enrollment.`);if(O(e,K(e,t))<.98)throw Error(`These samples differ too much from your original voice. Re-record your enrollment instead.`)}async function le(e){let t=(await u.seamsWalletDB.getDB()).transaction(d.appState,`readwrite`);try{let n=await t.store.get(U(e)),r=G(n?.value,e);return r&&JSON.stringify(n?.value?.adaptation)!==JSON.stringify(r.adaptation)&&(r=Z(r,r.adaptation),await t.store.put({key:U(e),value:r})),await t.done,r}catch(e){try{t.abort()}catch{}throw await t.done.catch(X),e}}async function ue(){let e=await u.getAppState(R);if(e===void 0)return null;let t=b(e,`Previous VoiceID enrollment`);return t.kind!==`browser_voiceid_enrollment_v1`||t.model!==w?null:W(t.template)}async function J(e,t,n){let r=y(await e.objectStore(d.walletSelections).get(t.walletId)),i=oe(await e.objectStore(d.walletAuthorities).get(t.authorityId)),a=await e.objectStore(d.walletAuthMethods).get(t.walletAuthMethodId),o=v(a);if(a!==void 0&&!o)throw Error(`VoiceID method storage is invalid.`);if(!i||i.record.state!==`active`||i.record.walletId!==t.walletId)throw Error(`VoiceID authority changed. Start again.`);if(o&&(o.record.kind!==`voiceid`||o.record.status!==`active`||o.record.walletId!==t.walletId||o.record.walletAuthorityId!==t.authorityId||o.record.deviceKeyId!==t.deviceKeyId)||(n===null||n.target===`enrolled`)&&!o)throw Error(`VoiceID method is unavailable.`);if((await e.objectStore(d.appState).get(N(t.walletId,t.walletAuthMethodId)))?.value?.deviceKeyId!==t.deviceKeyId)throw Error(`VoiceID device changed. Start again.`);let s=ae((await e.objectStore(d.appState).get(p(t.walletId,t.walletAuthMethodId)))?.value);if(!s.enabled)throw Error(`VoiceID is turned off.`);if(n){if(!se(h({permissions:i.record.permissions}),h({permissions:re()})))throw Error(`Full owner authority is required to change VoiceID enrollment.`);let a=v(await e.objectStore(d.walletAuthMethods).get(n.sessionMethodId));if(n.expiresAtMs<=Date.now()||s.revision!==n.availabilityRevision||!r||r.record.lockState!==`unlocked`||r.record.lockGeneration!==n.lockGeneration||r.record.walletAuthMethodId!==n.sessionMethodId||!a||a.record.status!==`active`||n.target===`activating`&&a.record.kind===`voiceid`||a.record.walletId!==t.walletId||a.record.walletAuthorityId!==t.authorityId)throw Error(`The owner session changed or expired. Start again.`);if(n.target===`enrolled`){let r=v(await e.objectStore(d.walletAuthMethods).get(n.confirmationMethodId));if(!r||r.record.kind!==`passkey`||r.record.status!==`active`||r.record.walletId!==t.walletId||r.record.walletAuthorityId!==t.authorityId)throw Error(`The confirming passkey changed or was removed. Start again.`)}}}async function Y(e,t){let n=(await u.seamsWalletDB.getDB()).transaction(B,`readwrite`);try{await J(n,e,t),await n.done}catch(e){throw $(n),await n.done.catch(X),e}}function X(){}async function de(e,t){let n=e.scope,r=e.kind===`activation`?e.owner:null;if(r&&r.target!==`activating`)throw Error(`VoiceID activation requires its owner scope.`);let i=(await u.seamsWalletDB.getDB()).transaction(B,`readwrite`);try{await J(i,n,r);let e=i.objectStore(d.appState),a=await e.get(U(n)),o=G(a?.value,n);if(!o)throw Error(`Unlock with a passkey, then open Manage voice to import or record your enrollment.`);t.throwIfAborted();let s=o.adaptation,c=s.kind===`adapted`?K(o.baseline,s.samples):[...o.baseline];return JSON.stringify(a?.value?.adaptation)!==JSON.stringify(s)&&await e.put({key:U(n),value:Z(o,s)}),await i.done,t.throwIfAborted(),{template:c,baseline:[...o.baseline],kind:s.kind}}catch(e){try{i.abort()}catch{}throw await i.done.catch(X),e}}function Z(e,t){return{kind:`browser_voiceid_enrollment_v2`,scope:e.scope,model:e.model,generation:e.generation,revision:e.revision+1,baseline:e.baseline,adaptation:t}}async function Q(e,t,n,r,i){let a;if(r.kind===`activate`){if(t.target!==`activating`||n.kind!==`missing`)throw Error(`VoiceID activation cannot replace an enrollment.`)}else{if(t.target!==`enrolled`)throw Error(`VoiceID changes require a confirmation guard.`);await r.confirmation.consume(e,t,n,r.kind,i)}if(r.kind===`import`&&(t.target!==`enrolled`||n.kind!==`missing`))throw Error(`Import requires an existing method without a scoped enrollment.`);if(r.kind===`replace`||r.kind===`activate`||r.kind===`import`)a={kind:`browser_voiceid_enrollment_v2`,scope:e,model:w,generation:crypto.randomUUID(),revision:0,baseline:W(r.baseline),adaptation:{kind:`baseline`}};else{if(n.kind!==`present`)throw Error(`Record or import your original voice first.`);r.kind===`improve`?(q(n.enrollment.baseline,r.samples),a=Z(n.enrollment,{kind:`adapted`,samples:r.samples})):a=Z(n.enrollment,{kind:`baseline`})}if(new TextEncoder().encode(JSON.stringify(a)).length>32768)throw Error(`VoiceID enrollment exceeds its storage limit.`);let o=await u.seamsWalletDB.getDB();i.throwIfAborted();let s=o.transaction(B,`readwrite`),c=$.bind(null,s);i.addEventListener(`abort`,c,{once:!0});try{await J(s,e,t);let o=s.objectStore(d.appState),c=G((await o.get(U(e)))?.value,e);if(n.kind===`missing`?c!==null:!c||c.generation!==n.enrollment.generation||c.revision!==n.enrollment.revision)throw Error(`VoiceID enrollment changed in another operation. Start again.`);i.throwIfAborted(),await o.put({key:U(e),value:a}),r.kind===`import`&&await fe(s),await s.done}catch(e){throw c(),await s.done.catch(X),e}finally{i.removeEventListener(`abort`,c)}}function $(e){try{e.abort()}catch{}}async function fe(e){let t=e.objectStore(d.appState),n=await e.objectStore(d.walletAuthMethods).getAll();for(let e of n){if(!g(e)||e.kind!==`voiceid`)continue;let n=v(e);if(!n)return;let r=n.record;if(r.kind!==`voiceid`||r.status!==`active`)continue;let i={walletId:r.walletId,authorityId:r.walletAuthorityId,walletAuthMethodId:r.walletAuthMethodId,deviceKeyId:r.deviceKeyId};try{if(!G((await t.get(U(i)))?.value,i))return}catch{return}}await t.delete(R)}async function pe(e,t,n){let r=document.querySelector(`.seams-voiceid-activation-surface`);if(!(r instanceof HTMLElement))throw Error(`Open VoiceID activation in the Wallet.`);await M(r,t,n),t.throwIfAborted();let i=new me(r,e,t,n);try{await i.result}finally{i.dispose()}}var me=class{completion=new f;result=this.completion.promise;status;prompt;phrase=o(null);record;cancel;samples=[];phase=`ready`;get closed(){return this.phase===`closed`}controller=new AbortController;guardTimer;checkingGuard=!1;constructor(e,t,n,r){this.plan=t,this.signal=n,this.model=r,this.prompt=new l(document,`Enroll your voice in this browser`,this.phrase),this.prompt.title.textContent=`Read aloud · 0 of 3 accepted`,this.status=this.prompt.status,this.record=this.prompt.record,this.cancel=this.prompt.cancel,this.record.textContent=`Record sample 1`,this.record.addEventListener(`click`,this.capture),this.cancel.setAttribute(`aria-label`,`Cancel enrollment`),this.cancel.addEventListener(`click`,this.cancelled),this.prompt.ready(t.kind===`improve`?`Read naturally. Your original enrollment stays unchanged.`:`Read the full phrase at your normal pace.`),e.replaceChildren(this.prompt.element),n.addEventListener(`abort`,this.cancelled,{once:!0}),this.guardTimer=setInterval(this.checkGuard,250),n.aborted?this.cancelled():this.record.focus()}capture=()=>{this.collect()};async collect(){if(this.phase!==`ready`)return;let e=this.samples.length+1;try{c(),this.phase=`recording`,this.record.disabled=!0,this.status.textContent=`Opening microphone…`;let e=await t(this.controller.signal,this.prompt.capture);if(this.signal.aborted||this.closed){e.kind===`captured`&&e.samples.fill(0);return}if(e.kind===`insufficient_audio`){this.status.textContent=n(e.quality);return}if(this.plan.kind===`improve`&&e.quality.noise!==`normal`){e.samples.fill(0),this.status.textContent=`Use a quiet place to improve your voice enrollment.`;return}let a;try{if(await Y(this.plan.scope,this.plan.guard),this.status.textContent=`Checking your voice…`,(await i(e.samples,this.phrase,this.model)).kind===`mismatch`)throw new r;a=await E(e.samples)}finally{e.samples.fill(0)}if(this.signal.aborted||this.closed){a.fill(0);return}if(this.plan.kind===`improve`&&O(this.plan.snapshot.enrollment.baseline,a)<.45)throw a.fill(0),Error(`Your voice did not match the original enrollment. To replace it, return to Manage voice and choose Record voice again.`);for(let e of this.samples)if(O(e,a)<.45)throw a.fill(0),Error(`This sample differs from the earlier samples. Use the same microphone and try again.`);if(this.samples.push(a),this.samples.length<3){this.prompt.accepted(),this.status.textContent=`Sample ${this.samples.length} accepted. Record the next sample.`,this.record.textContent=`Record sample ${this.samples.length+1}`;return}let o=Array(192).fill(0);for(let e of this.samples)for(let t=0;t<o.length;t+=1)o[t]+=e[t]/3;this.phase=`committing`,this.prompt.title.textContent=`3 of 3 accepted`,this.status.textContent=`Saving voice enrollment…`;let s=this.plan;switch(s.kind){case`activate`:await Q(s.scope,s.guard,s.snapshot,{kind:`activate`,baseline:D(o)},this.controller.signal);break;case`replace`:await Q(s.scope,s.guard,s.snapshot,{kind:`replace`,baseline:D(o),confirmation:s.confirmation},this.controller.signal);break;case`improve`:await Q(s.scope,s.guard,s.snapshot,{kind:`improve`,samples:this.samples,confirmation:s.confirmation},this.controller.signal);break}if(this.signal.aborted||this.closed)return;this.phase=`closed`,this.prompt.accepted(),this.status.textContent=`Voice enrollment saved.`,this.completion.resolve()}catch(e){if(!this.closed&&this.phase===`committing`){this.phase=`closed`,this.controller.abort(),this.completion.reject(S(e));return}!this.closed&&this.samples.length===3&&this.samples.pop()?.fill(0),this.closed||(this.status.textContent=e instanceof Error?e.message:`VoiceID enrollment failed.`)}finally{this.closed||(this.samples.length<e&&(this.record.textContent=`Retry sample ${e}`,this.status.textContent=`Sample ${e} was not accepted. ${this.status.textContent??``}`),this.phrase=o(this.phrase),this.prompt.setPhrase(this.phrase),this.prompt.title.textContent=`Read aloud · ${this.samples.length} of 3 accepted`,this.prompt.ready(this.status.textContent??``),this.phase=`ready`,this.record.disabled=!1)}}cancelled=()=>{this.closed||(this.phase=`closed`,this.controller.abort(),this.completion.reject(Error(`VoiceID enrollment was cancelled.`)))};checkGuard=()=>{this.closed||this.checkingGuard||(this.checkingGuard=!0,this.validateGuard())};async validateGuard(){try{await Y(this.plan.scope,this.plan.guard)}catch(e){this.closed||(this.phase=`closed`,this.controller.abort(),this.completion.reject(S(e)))}finally{this.checkingGuard=!1}}dispose(){this.phase=`closed`,this.controller.abort(),clearInterval(this.guardTimer),this.signal.removeEventListener(`abort`,this.cancelled),this.prompt.dispose(),this.record.removeEventListener(`click`,this.capture),this.cancel.removeEventListener(`click`,this.cancelled);for(let e of this.samples)e.fill(0)}};async function he(e,n,r,a,o){let s=await t(n,r);if(s.kind===`insufficient_audio`)return n.throwIfAborted(),s;try{n.throwIfAborted();let t=await i(s.samples,a,o),r=await E(s.samples),c;try{n.throwIfAborted();let t=await de(e,n);try{c=O(r,t.template)}finally{t.template.fill(0),t.baseline.fill(0)}}finally{r.fill(0)}return c<.45?{kind:`mismatch`,quality:s.quality}:t.kind===`matched`?{kind:`matched`}:{kind:`phrase_mismatch`}}finally{s.samples.fill(0)}}export{ue as a,V as c,F as d,M as f,Q as i,ce as l,he as n,le as o,Y as r,H as s,pe as t,L as u};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{d as e,h as t,i as n,l as r,n as i,r as a,t as o,y as s}from"./prompt-
|
|
1
|
+
import{d as e,h as t,i as n,l as r,n as i,r as a,t as o,y as s}from"./prompt-Ch0glUJT.js";import{SeamsWordmark as c,VOICE_ID_HOST_API_VERSION as l,WorkerDeferred as u,authMenuSuccessCopy as d,backIcon as f,createCspStylesheetManager as p,createWalletIframeSurfaceMeasurementReporter as m,getDefaultCspNonce as h,toError as g}from"../voiceid-host.js";const _=new WeakMap;var v=class{footer;constructor(e,t){this.prompt=t;let n=e.ownerDocument,r=_.get(n);r||(r=p({doc:n,baseCss:`
|
|
2
2
|
.seams-voiceid-activation-surface.seams-voiceid-unlock {
|
|
3
3
|
box-sizing: border-box;
|
|
4
4
|
overflow: hidden;
|
|
@@ -77,4 +77,4 @@ import{d as e,h as t,i as n,l as r,n as i,r as a,t as o,y as s}from"./prompt-vzt
|
|
|
77
77
|
color: var(--seams-colors-textPrimary, #eff2f3);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
`,nonce:h}),_.set(n,r)),r.ensureBase(),e.classList.add(`seams-voiceid-unlock`);let o=n.createElement(`div`);o.className=`seams-voiceid-unlock-header`,t.cancel.className=`seams-voiceid-unlock-back`,t.cancel.setAttribute(`aria-label`,`Back`),t.cancel.replaceChildren(),i(f(),t.cancel),o.append(t.cancel),e.prepend(o),this.footer=n.createElement(`div`),this.footer.className=`seams-voiceid-unlock-footer`,i([a(`span`,null,`Secured by`),` `,a(c,{})],this.footer),e.append(this.footer)}succeed(){let e=this.prompt.cancel;return e.animate([{opacity:1},{opacity:0}],{duration:150,easing:`ease-in`,fill:`forwards`}),{top:e.getBoundingClientRect().top,bottom:this.footer.getBoundingClientRect().top}}dispose(){i(null,this.prompt.cancel),i(null,this.footer)}},y=class{result;completion=new u;controller=new AbortController;element;activateButton;cancelButton;status;prompt;unlockFrame;unmountCapture;measurement;phrase=t(null);state={kind:`preparing`};settlement=`closed`;expiryTimer=null;constructor(e,t,n,r){let i=t===`activation`?`Activate VoiceID`:`Unlock with VoiceID`,a=document.getElementById(`root`)??document.body;if(!a)throw Error(`Wallet activation surface has no mount root.`);this.element=document.createElement(`section`),this.element.className=`seams-wallet-ui seams-auth-method-activation-surface seams-voiceid-activation-surface`,this.element.setAttribute(`role`,`dialog`),this.element.setAttribute(`aria-modal`,`true`),this.element.setAttribute(`aria-label`,i),this.prompt=new o(document,i,this.phrase),this.element.append(this.prompt.element),this.unlockFrame=t===`unlock`?new v(this.element,this.prompt):null,this.status=this.prompt.status,this.status.textContent=`Loading VoiceID…`,this.prompt.title.textContent=`VoiceID`,this.element.dataset.preparation=`loading`,this.activateButton=this.prompt.record,this.activateButton.textContent=i,this.activateButton.disabled=!0,this.activateButton.hidden=this.
|
|
80
|
+
`,nonce:h}),_.set(n,r)),r.ensureBase(),e.classList.add(`seams-voiceid-unlock`);let o=n.createElement(`div`);o.className=`seams-voiceid-unlock-header`,t.cancel.className=`seams-voiceid-unlock-back`,t.cancel.setAttribute(`aria-label`,`Back`),t.cancel.replaceChildren(),i(f(),t.cancel),o.append(t.cancel),e.prepend(o),this.footer=n.createElement(`div`),this.footer.className=`seams-voiceid-unlock-footer`,i([a(`span`,null,`Secured by`),` `,a(c,{})],this.footer),e.append(this.footer)}succeed(){let e=this.prompt.cancel;return e.animate([{opacity:1},{opacity:0}],{duration:150,easing:`ease-in`,fill:`forwards`}),{top:e.getBoundingClientRect().top,bottom:this.footer.getBoundingClientRect().top}}dispose(){i(null,this.prompt.cancel),i(null,this.footer)}},y=class{result;completion=new u;controller=new AbortController;element;activateButton;cancelButton;status;prompt;unlockFrame;autoStartUnlock;unmountCapture;measurement;phrase=t(null);state={kind:`preparing`};settlement=`closed`;expiryTimer=null;constructor(e,t,n,r){let i=t===`activation`?`Activate VoiceID`:`Unlock with VoiceID`,a=document.getElementById(`root`)??document.body;if(!a)throw Error(`Wallet activation surface has no mount root.`);this.element=document.createElement(`section`),this.element.className=`seams-wallet-ui seams-auth-method-activation-surface seams-voiceid-activation-surface`,this.element.setAttribute(`role`,`dialog`),this.element.setAttribute(`aria-modal`,`true`),this.element.setAttribute(`aria-label`,i),this.prompt=new o(document,i,this.phrase),this.element.append(this.prompt.element),this.unlockFrame=t===`unlock`?new v(this.element,this.prompt):null,this.autoStartUnlock=this.unlockFrame!==null&&r!==void 0,this.status=this.prompt.status,this.status.textContent=`Loading VoiceID…`,this.prompt.title.textContent=`VoiceID`,this.element.dataset.preparation=`loading`,this.activateButton=this.prompt.record,this.activateButton.textContent=i,this.activateButton.disabled=!0,this.activateButton.hidden=this.autoStartUnlock,this.activateButton.addEventListener(`click`,this.begin),this.cancelButton=this.prompt.cancel,this.cancelButton.addEventListener(`click`,this.cancel),this.element.addEventListener(`cancel`,this.cancel),this.unmountCapture=r?r(this.element):null,r||a.appendChild(this.element),this.measurement=!r&&n.kind===`wallet_iframe`?m({kind:`request_surface`,element:this.element,requestId:n.requestId,postMeasurement:n.postMeasurement}):null,this.cancelButton.focus(),this.result=this.completion.promise,this.initialize(e)}dispose(){this.measurement?.disconnect(),this.expiryTimer!==null&&clearTimeout(this.expiryTimer),this.element.removeEventListener(`cancel`,this.cancel),this.activateButton.removeEventListener(`click`,this.begin),this.cancelButton.removeEventListener(`click`,this.cancel),this.unlockFrame?.dispose(),this.prompt.dispose(),this.unmountCapture?this.unmountCapture(this.settlement):this.element.remove()}async initialize(e){try{await new Promise(S),this.controller.signal.throwIfAborted();let t=await e(this.controller.signal,this.prompt.progress);if(this.state.kind===`closed`)return;if(t.kind===`management`){this.element.setAttribute(`aria-label`,`Manage voice`),this.element.dataset.preparation=`ready`,this.state={kind:`capturing`};let e=await t.action(this.controller.signal);this.controller.signal.throwIfAborted(),this.state={kind:`closed`},this.completion.resolve(e);return}let n=t.expiresAtMs===null?null:t.expiresAtMs-Date.now();if(n!==null&&n<=0)throw Error(`VoiceID challenge expired. Start unlock again.`);this.state={kind:`ready`,capture:t},this.expiryTimer=n===null?null:setTimeout(this.expire,n);for(let e of this.element.querySelectorAll(`.seams-voiceid-model-download`))e.remove();this.element.dataset.preparation=`ready`,this.prompt.title.textContent=`Read aloud`,this.status.textContent=`Read naturally, then pause.`,this.activateButton.disabled=!1,this.autoStartUnlock?this.begin():this.activateButton.focus()}catch(e){if(this.state.kind===`closed`)return;this.state={kind:`closed`},this.completion.reject(g(e))}}begin=()=>{if(this.state.kind!==`ready`)return;let e=this.state.capture;this.state={kind:`capturing`},this.perform(e)};async perform(i){this.activateButton.disabled=!0,this.status.textContent=`Opening microphone…`;try{let e=await i.action(this.controller.signal,this.prompt.capture,this.phrase);if(this.state.kind===`closed`)return;this.unlockFrame&&await this.showUnlocked(this.unlockFrame),this.state={kind:`closed`},this.completion.resolve(e)}catch(a){if(this.state.kind===`closed`)return;if(a instanceof n||a instanceof e||a instanceof r||a instanceof s){if(i.expiresAtMs!==null&&Date.now()>=i.expiresAtMs){this.expire();return}let e=this.phrase;this.phrase=t(e),this.prompt.setPhrase(this.phrase),this.state={kind:`ready`,capture:i},this.activateButton.hidden=!1,this.activateButton.textContent=`Try again`,this.prompt.ready(a.message),this.activateButton.focus();return}this.state={kind:`closed`},this.completion.reject(g(a))}}async showUnlocked(e){this.state={kind:`succeeding`},this.settlement=`succeeded`,this.expiryTimer!==null&&clearTimeout(this.expiryTimer),this.expiryTimer=null,this.cancelButton.disabled=!0;let t=performance.now(),n=d(`authenticated`);await this.prompt.succeed(n.headline,n.message,e.succeed());let r=Math.max(0,1300-(performance.now()-t));await this.element.animate([{opacity:1},{opacity:1,offset:r/(r+240),easing:`ease-in`},{opacity:0}],{duration:r+240,fill:`forwards`}).finished}cancel=()=>{this.state.kind===`closed`||this.state.kind===`succeeding`||(this.state={kind:`closed`},this.controller.abort(),this.completion.reject(new b))};expire=()=>{this.state.kind===`closed`||this.state.kind===`succeeding`||(this.state={kind:`closed`},this.controller.abort(),this.completion.reject(Error(`VoiceID challenge expired. Start unlock again.`)))}},b=class extends Error{constructor(){super(`VoiceID capture was cancelled.`),this.name=`VoiceIdCaptureCancelledError`}};async function x(e){let t=new y(e.prepare,e.purpose,e.measurement,e.mountCapture);try{return await t.result}finally{t.dispose()}}function S(e){requestAnimationFrame(C.bind(null,e))}function C(e){setTimeout(e,0)}const w=3;if(l!==3)throw Error(`VoiceID requires a matching Wallet host API.`);function T(e){return{unlock:E.bind(null,e),activate:O.bind(null,e),review:A.bind(null,e)}}function E(e,t){return x({purpose:`unlock`,measurement:t.surfaceMeasurementBinding,mountCapture:t.mountCapture,prepare:D.bind(null,e,t)})}async function D(e,t,n,r){let i=await import(`./unlock-qE4fk0Bk.js`);return n.throwIfAborted(),i.prepareUnlockSurface(t,n,r,e)}async function O(e,t){try{return await x({purpose:`activation`,measurement:t.context.signingEngine.getWalletIframeSurfaceMeasurementBinding(),prepare:k.bind(null,e,t)})}catch(e){if(e instanceof b)return{kind:`cancelled`};throw e}}async function k(e,t,n){let r=await import(`./addVoiceId-8W2LeLae.js`);return n.throwIfAborted(),r.prepareActivationSurface(t,n,e)}async function A(e,t,n){return(await import(`./approval-CvM_A5Q4.js`)).voiceIdPolicyForRequest(t,n,e)}export{T as createVoiceIdHostPlugin,w as hostApiVersion};
|
|
@@ -611,4 +611,4 @@ ${Ne.split(`{id}`).join(ze)}`:en}),J(`div`,{...Le,ref:an,"data-voice-beam":ze,"d
|
|
|
611
611
|
.seams-voiceid-close:hover { background: var(--seams-colors-surface, #202b31); }
|
|
612
612
|
.seams-voiceid-primary:hover:not(:disabled) { opacity: 0.9; }
|
|
613
613
|
}
|
|
614
|
-
`,nonce:r}),Qr.set(e,o)),o.ensureBase(),this.element=e.createElement(`section`),this.element.className=`seams-voiceid-prompt`,this.element.setAttribute(`aria-label`,t),this.title=e.createElement(`h2`),this.title.textContent=`Read aloud`,this.phrase=e.createElement(`p`),this.phrase.className=`seams-voiceid-phrase`,this.phrase.dataset.voiceidPhrase=``,this.phrase.setAttribute(`aria-live`,`polite`),this.setPhrase(i),this.visual=new Zr(e,this.element,a),this.status=e.createElement(`p`),this.status.className=`seams-voiceid-status`,this.status.setAttribute(`role`,`status`),this.record=e.createElement(`button`),this.record.type=`button`,this.record.className=`seams-voiceid-primary`,this.record.textContent=`Record`,this.cancel=e.createElement(`button`),this.cancel.type=`button`,this.cancel.className=`seams-voiceid-close`;let s=e.createElementNS(`http://www.w3.org/2000/svg`,`svg`);s.setAttribute(`viewBox`,`0 0 24 24`),s.setAttribute(`width`,`20`),s.setAttribute(`height`,`20`),s.setAttribute(`aria-hidden`,`true`);let c=e.createElementNS(`http://www.w3.org/2000/svg`,`path`);c.setAttribute(`d`,`M6 6l12 12M18 6L6 18`),c.setAttribute(`fill`,`none`),c.setAttribute(`stroke`,`currentColor`),c.setAttribute(`stroke-width`,`1.75`),c.setAttribute(`stroke-linecap`,`round`),s.append(c),this.cancel.append(s),this.cancel.setAttribute(`aria-label`,`Cancel`),this.progress=e.createElement(`div`),this.progress.className=`seams-voiceid-progress-slot`;let l=e.createElement(`div`);l.className=`seams-voiceid-record-slot`,this.microphone=e.createElement(`button`),this.microphone.type=`button`,this.microphone.className=`seams-voiceid-microphone`,this.microphone.textContent=`Use system microphone`,this.microphone.hidden=!0,this.microphone.addEventListener(`click`,this.changeMicrophone),l.append(this.record,this.microphone),this.element.append(this.visual.glow,this.cancel,this.title,this.phrase,this.visual.element,this.status,l,this.progress)}setPhrase(e){let t=$r.get(e)??e;this.phrase.textContent=t.charAt(0).toUpperCase()+t.slice(1)+`.`,this.phrase.dataset.voiceidPhrase=e}capture=e=>{if(!this.disposed)switch(e.kind){case`opening`:this.microphoneRequested=!0,this.microphone.hidden=!0;return;case`listening`:this.microphone.hidden=!0,
|
|
614
|
+
`,nonce:r}),Qr.set(e,o)),o.ensureBase(),this.element=e.createElement(`section`),this.element.className=`seams-voiceid-prompt`,this.element.setAttribute(`aria-label`,t),this.title=e.createElement(`h2`),this.title.textContent=`Read aloud`,this.phrase=e.createElement(`p`),this.phrase.className=`seams-voiceid-phrase`,this.phrase.dataset.voiceidPhrase=``,this.phrase.setAttribute(`aria-live`,`polite`),this.setPhrase(i),this.visual=new Zr(e,this.element,a),this.status=e.createElement(`p`),this.status.className=`seams-voiceid-status`,this.status.setAttribute(`role`,`status`),this.record=e.createElement(`button`),this.record.type=`button`,this.record.className=`seams-voiceid-primary`,this.record.textContent=`Record`,this.cancel=e.createElement(`button`),this.cancel.type=`button`,this.cancel.className=`seams-voiceid-close`;let s=e.createElementNS(`http://www.w3.org/2000/svg`,`svg`);s.setAttribute(`viewBox`,`0 0 24 24`),s.setAttribute(`width`,`20`),s.setAttribute(`height`,`20`),s.setAttribute(`aria-hidden`,`true`);let c=e.createElementNS(`http://www.w3.org/2000/svg`,`path`);c.setAttribute(`d`,`M6 6l12 12M18 6L6 18`),c.setAttribute(`fill`,`none`),c.setAttribute(`stroke`,`currentColor`),c.setAttribute(`stroke-width`,`1.75`),c.setAttribute(`stroke-linecap`,`round`),s.append(c),this.cancel.append(s),this.cancel.setAttribute(`aria-label`,`Cancel`),this.progress=e.createElement(`div`),this.progress.className=`seams-voiceid-progress-slot`;let l=e.createElement(`div`);l.className=`seams-voiceid-record-slot`,this.microphone=e.createElement(`button`),this.microphone.type=`button`,this.microphone.className=`seams-voiceid-microphone`,this.microphone.textContent=`Use system microphone`,this.microphone.hidden=!0,this.microphone.addEventListener(`click`,this.changeMicrophone),l.append(this.record,this.microphone),this.element.append(this.visual.glow,this.cancel,this.title,this.phrase,this.visual.element,this.status,l,this.progress)}setPhrase(e){let t=$r.get(e)??e;this.phrase.textContent=t.charAt(0).toUpperCase()+t.slice(1)+`.`,this.phrase.dataset.voiceidPhrase=e}capture=e=>{if(!this.disposed)switch(e.kind){case`opening`:this.microphoneRequested=!0,this.microphone.hidden=!0;return;case`listening`:this.microphone.hidden=!0,this.element.dataset.capture=`listening`,this.record.hidden=!0,this.status.textContent=`Listening`,this.visual.listen();return;case`activity`:this.visual.add(e);return;case`measured`:e.quality.noise===`high`&&(this.status.textContent=`Background noise is high. Move closer to the microphone.`);return;case`processing`:this.element.dataset.capture=`processing`,this.lockLayout(),this.visual.checking(),this.status.textContent=`Checking your voice…`;return;default:return oi(e)}};accepted(){this.visual.accept()}async succeed(e,t,n=this.element.getBoundingClientRect()){this.disposed||(this.element.dataset.capture=`succeeded`,this.record.hidden=!0,this.microphone.hidden=!0,await this.visual.succeed(this.beginSuccess.bind(this,e,t,n)))}ready(e){this.unlockLayout(),this.visual.ready(),this.element.dataset.capture=`ready`,this.status.textContent=e,this.record.hidden=!1,this.record.disabled=!1,this.microphone.hidden=!this.microphoneRequested}changeMicrophone=()=>{this.useSystemMicrophone()};async useSystemMicrophone(){this.record.disabled=!0,this.microphone.disabled=!0,await be(),!this.disposed&&(this.status.textContent=`The next recording will use your system microphone.`,this.record.disabled=!1,this.microphone.disabled=!1,this.microphone.hidden=!0,this.record.focus())}dispose(){this.disposed=!0,this.microphone.removeEventListener(`click`,this.changeMicrophone),this.visual.stop()}lockLayout(){for(let e of[this.element,this.phrase,this.status])e.style.minHeight=`${e.getBoundingClientRect().height}px`;this.element.style.height=this.element.style.minHeight}async beginSuccess(e,t,n){if(this.disposed)return;let r=this.visual.element.getBoundingClientRect(),i=(n.top+n.bottom-r.top-r.bottom)/2;if(this.visual.element.animate([{translate:`0 0`},{translate:`0 ${i}px`}],{duration:600,easing:`cubic-bezier(0.2, 0, 0, 1)`,fill:`forwards`}),await Promise.all([this.title,this.phrase,this.status].map(ii)),this.disposed)return;this.phrase.textContent=e,this.phrase.classList.add(`seams-voiceid-success-headline`),this.status.textContent=t,this.status.classList.add(`seams-voiceid-success-message`);let a=matchMedia(`(prefers-reduced-motion: reduce)`).matches;for(let[e,t]of[this.phrase,this.status].entries())t.style.translate=`0 ${i}px`,ai(t,a,ni-ti+e*ri)}unlockLayout(){for(let e of[this.element,this.phrase,this.status])e.style.minHeight=``;this.element.style.height=``}};const ti=150,ni=450,ri=40;function ii(e){return e.animate([{opacity:1},{opacity:0}],{duration:150,easing:`ease-in`,fill:`forwards`}).finished}function ai(e,t,n){e.animate([{opacity:0,transform:t?`none`:`translateY(4px)`},{opacity:1,transform:`none`}],{delay:n,duration:300,easing:`cubic-bezier(0.2, 0, 0, 1)`,fill:`both`})}function oi(e){throw Error(`Unexpected capture event: ${String(e)}`)}export{c as _,ke as a,Ce as c,P as d,ce as f,m as g,w as h,we as i,Se as l,T as m,bt as n,Oe as o,S as p,Xe as r,De as s,ei as t,le as u,p as v,a as y};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{s as e}from"./prompt-Ch0glUJT.js";import{a as t,n}from"./deviceBridge-CcnU-PrE.js";import{f as r,o as i,s as a}from"./enrollment-4Pyn_EE8.js";import{IndexedDBManager as o,fetchWithWalletHomeDiscovery as s,readAuthMethodEnabled as c}from"../voiceid-host.js";async function l(e){let{authMethod:t,challenge:n}=e;if(n.walletId!==t.walletId||n.walletAuthMethodId!==t.walletAuthMethodId||n.walletAuthorityId!==t.walletAuthorityId||n.deviceKeyId!==t.deviceKeyId)throw Error(`VoiceID challenge does not match the selected device method.`);let r=await e.capture({challenge:n,signal:e.signal,onCapture:e.onCapture,phrase:e.phrase});try{if(e.signal.aborted)throw Error(`VoiceID unlock was cancelled.`);let n=await e.http.verify({assertion:r.assertion,signal:e.signal});if(e.signal.aborted)throw Error(`VoiceID unlock was cancelled.`);if(n.walletSession.walletId!==t.walletId||n.walletSession.authMethodId!==t.walletAuthMethodId||n.walletSession.authorityId!==t.walletAuthorityId||n.custodyEnvelope.factor.deviceKeyId!==t.deviceKeyId)throw Error(`VoiceID server verified a different device method.`);return await e.install(n,r.factorSecret32)}finally{r.factorSecret32.fill(0)}}async function u(e,n,l,u){if(!await c(e.walletId,e.walletAuthMethodId))throw Error(`VoiceID is turned off in this browser. Enable it in Authentication methods.`);let d=await e.prepareSession(n),p=await o.getWalletAuthMethodV2(e.walletAuthMethodId);if(!p||p.kind!==`voiceid`||p.status!==`active`||p.walletId!==e.walletId||p.walletAuthMethodId!==e.walletAuthMethodId)throw Error(`Activate VoiceID on this device before unlocking this wallet.`);let m=new t(e.relayerUrl,location.origin,s);if(!await i(await a(e.walletId,e.walletAuthMethodId)))throw Error(`Unlock with a passkey, then open Manage voice to import or record your enrollment.`);await r(l,n,u),n.throwIfAborted(),e.assertActive();let h=await m.challenge({walletId:e.walletId,walletAuthMethodId:e.walletAuthMethodId,signal:AbortSignal.any([n,AbortSignal.timeout(15e3)])});e.assertActive();let g={authMethod:p,challenge:h,http:m,install:d,initiation:e.mountCapture?`hosted_menu`:`sdk_request`};return n.throwIfAborted(),{kind:`recording`,expiresAtMs:h.expiresAtMs,action:f.bind(null,u,g)}}function d(e,t,n,r){return e({walletSession:n.walletSession,operationCredential:n.operationCredential,custodyEnvelope:n.custodyEnvelope,factorSecret32:r,signal:t,near:n.near})}async function f(t,r,i,a,o){switch(r.initiation){case`sdk_request`:e();break;case`hosted_menu`:break;default:return p(r.initiation)}return await l({authMethod:r.authMethod,challenge:r.challenge,http:r.http,capture:n.bind(null,t),install:d.bind(null,r.install,i),signal:i,onCapture:a,phrase:o})}function p(e){throw Error(`Unexpected VoiceID initiation: ${String(e)}`)}export{u as prepareUnlockSurface};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seams/voiceid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Optional browser VoiceID authentication for Seams Wallet",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -50,17 +50,21 @@
|
|
|
50
50
|
"voice-glow": "0.2.1"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@seams/wallet": "0.
|
|
54
|
-
"react": "
|
|
53
|
+
"@seams/wallet": "0.31.0",
|
|
54
|
+
"react": "19.1.1",
|
|
55
|
+
"react-dom": "19.1.1"
|
|
55
56
|
},
|
|
56
57
|
"peerDependenciesMeta": {
|
|
57
58
|
"react": {
|
|
58
59
|
"optional": true
|
|
60
|
+
},
|
|
61
|
+
"react-dom": {
|
|
62
|
+
"optional": true
|
|
59
63
|
}
|
|
60
64
|
},
|
|
61
65
|
"devDependencies": {
|
|
62
66
|
"@seams/wallet": "workspace:*",
|
|
63
|
-
"rolldown": "
|
|
67
|
+
"rolldown": "1.0.0-rc.9"
|
|
64
68
|
},
|
|
65
69
|
"bin": {
|
|
66
70
|
"seams-voiceid-host": "dist/compose-host.mjs"
|
package/src/README.md
CHANGED
|
@@ -207,8 +207,9 @@ same hash-verified browser cache as the models. If storage is unavailable, loadi
|
|
|
207
207
|
still works for the current session. Transaction approval uses the same preparation
|
|
208
208
|
before starting its bounded recording attempt.
|
|
209
209
|
The lightweight shell itself still needs its first module download.
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
The Wallet auth menu starts listening after the user selects VoiceID and preparation
|
|
211
|
+
finishes. A direct SDK unlock request waits for the user to select Unlock with VoiceID
|
|
212
|
+
inside the Wallet. Back cancels capture and returns to the login menu.
|
|
212
213
|
Retries require Try again. The card uses the wallet's Back icon and Seams wordmark,
|
|
213
214
|
with one rounded surface and a transparent activation container.
|
|
214
215
|
Cancellation aborts the challenge request and prevents late UI or recording.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,p as s,s as c,t as l,u,v as d}from"./prompt-vztg41jG.js";import{IndexedDBManager as f,SEAMS_WALLET_STORES as p,WorkerDeferred as m,availabilityKey as h,base64UrlDecode as g,base64UrlEncode as ee,buildDelegatedWalletAuthorityV1 as _,buildFullOwnerPermissionsV1 as v,isFiniteNumber as y,isPlainObject as b,parseStoredAuthMethodAvailability as x,parseVoiceIdDevicePublicKeyB64u as S,parseWalletAuthMethodV2StorageRow as C,parseWalletAuthorityStorageRow as te,parseWalletSelectionStorageRow as ne,requireRecord as w,sameDelegatedWalletAuthorityV1 as T,sha256DigestB64u as E,toError as D}from"../voiceid-host.js";const O=`e04dd7a6a1e23fc7b89e47682ea9d0a3946cf7bc59d07922c3db3efc52ae4269`,k=`redimnet-b1:${O}`,A=new a({path:`/sdk/voiceid/redimnet-b1.onnx`,sha256:O,bytes:8859593,label:`Speaker verification`});async function j(e){let t=await A.prepare(),n=new(await(import(`./ort.wasm.bundle.min-7y-hlRN8.js`))).Tensor(`float32`,e,[1,e.length]);try{let e=await t.run({samples:n});try{let t=e.embedding;if(!t||!(t.data instanceof Float32Array))throw Error(`VoiceID model returned an invalid embedding.`);return M(Array.from(t.data))}finally{for(let t of Object.values(e))t.dispose()}}finally{n.dispose()}}function M(e){if(e.length!==192)throw Error(`VoiceID template dimensions are invalid.`);let t=0;for(let n of e){if(!Number.isFinite(n))throw Error(`VoiceID template is invalid.`);t+=n*n}if(t<1e-12)throw Error(`VoiceID template contains no signal.`);let n=Math.sqrt(t),r=[];for(let t of e)r.push(t/n);return r}function N(e,t){if(e.length!==192||t.length!==192)throw Error(`VoiceID templates are incompatible.`);let n=0;for(let r=0;r<e.length;r+=1)n+=e[r]*t[r];return n}function re(e){return e.asset}function ie(e,t){return e+t.size}var ae=class{element=document.createElement(`section`);status=document.createElement(`p`);meter=document.createElement(`progress`);retryButton=document.createElement(`button`);retry=null;cancelled=new m;reveal;received=new Map;models;assets;total;constructor(e,t,n){this.parent=e,this.signal=t,this.models=[A,s(n),u],this.assets=[d,...this.models.map(re)],this.total=this.assets.reduce(ie,0),this.element.className=`seams-voiceid-model-download`,this.status.setAttribute(`role`,`status`),this.status.setAttribute(`aria-live`,`polite`),this.meter.setAttribute(`aria-label`,`VoiceID download`),this.meter.style.width=`100%`,this.retryButton.type=`button`,this.retryButton.className=`seams-voiceid-primary`,this.retryButton.textContent=`Retry loading`,this.retryButton.hidden=!0,this.retryButton.addEventListener(`click`,this.retryLoading),this.element.append(this.status,this.meter,this.retryButton),this.status.textContent=`Loading saved VoiceID…`,this.reveal=setTimeout(e.append.bind(e,this.element),300),t.addEventListener(`abort`,this.abort,{once:!0})}settle=()=>{clearTimeout(this.reveal),this.signal.removeEventListener(`abort`,this.abort),this.retryButton.removeEventListener(`click`,this.retryLoading)};abort=()=>{let e=new DOMException(`VoiceID preparation was cancelled.`,`AbortError`);this.cancelled.reject(e),this.retry?.reject(e)};retryLoading=()=>{this.retry?.resolve(),this.retry=null};async prepare(){this.signal.throwIfAborted(),await Promise.race([this.prepareUntilReady(),this.cancelled.promise])}async prepareUntilReady(){for(;;){this.signal.throwIfAborted(),this.retryButton.hidden=!0,this.meter.hidden=!1;try{for(let e of this.assets)await e.download(this.update.bind(this,e)),this.signal.throwIfAborted();this.preparing();for(let e of this.models)await e.prepare(),this.signal.throwIfAborted();this.ready();return}catch(t){if(this.signal.throwIfAborted(),!(t instanceof e))throw t;clearTimeout(this.reveal),this.parent.append(this.element),this.element.dataset.state=`failed`,this.meter.hidden=!0,this.status.textContent=t.message,this.retry=new m,this.retryButton.hidden=!1,this.retryButton.focus(),await this.retry.promise}}}update=(e,t)=>{switch(t.kind){case`checking_cache`:return;case`downloading`:this.received.set(e,t.received),this.element.dataset.state=`downloading`;break;case`ready`:this.received.set(e,e.size);break;default:}if(this.element.dataset.state!==`downloading`)return;let n=0;for(let e of this.received.values())n+=e;this.meter.max=this.total,this.meter.value=n,this.status.textContent=`Downloading VoiceID: ${(n/1e6).toFixed(1)} of ${(this.total/1e6).toFixed(1)} MB · ${Math.floor(n/this.total*100)}%`};preparing(){this.element.dataset.state=`preparing`,this.meter.removeAttribute(`value`),this.status.textContent=`Preparing VoiceID…`}ready(){this.element.dataset.state=`ready`,this.meter.hidden=!0,this.status.textContent=`VoiceID is ready.`}};async function P(e,t,n){let r=new ae(e,t,n);try{await r.prepare()}finally{r.settle()}}function F(e,t){return`voiceid-browser-device-v1:${encodeURIComponent(e)}:${encodeURIComponent(t)}`}function I(e,t,n){return new TextEncoder().encode(JSON.stringify([`seams-voiceid-browser-factor-v1`,location.origin,e,t,n]))}async function L(e,t){let n=await f.getAppState(F(e,t));if(n===void 0)return null;let r=w(n,`VoiceID device record`);if(r.kind!==`browser_voiceid_device_v1`||r.walletId!==e||r.walletAuthMethodId!==t)throw Error(`VoiceID device belongs to another wallet method.`);let{privateKey:i,wrappingKey:a,nonce:o,ciphertext:s}=r;if(!(i instanceof CryptoKey)||i.type!==`private`||i.extractable||i.algorithm.name!==`Ed25519`||!i.usages.includes(`sign`)||!(a instanceof CryptoKey)||a.type!==`secret`||a.extractable||a.algorithm.name!==`AES-GCM`||!a.usages.includes(`decrypt`)||!(o instanceof Uint8Array)||o.length!==12||!(s instanceof ArrayBuffer)||s.byteLength!==48)throw Error(`VoiceID device storage is invalid.`);let c=S(r.publicKeyB64u),l=await E(g(c));if(r.deviceKeyId!==l)throw Error(`VoiceID device key binding is invalid.`);return{kind:`browser_voiceid_device_v1`,walletId:e,walletAuthMethodId:t,privateKey:i,wrappingKey:a,nonce:new Uint8Array(o),ciphertext:s,publicKeyB64u:c,deviceKeyId:l}}async function oe(e,t){return await navigator.locks.request(F(e,t),se.bind(null,e,t))}async function se(e,t){let n=await L(e,t);if(n)return n;let r=await crypto.subtle.generateKey({name:`Ed25519`},!1,[`sign`,`verify`]),i=S(ee(new Uint8Array(await crypto.subtle.exportKey(`raw`,r.publicKey)))),a=await E(g(i)),o=await crypto.subtle.generateKey({name:`AES-GCM`,length:256},!1,[`encrypt`,`decrypt`]),s=crypto.getRandomValues(new Uint8Array(32)),c=crypto.getRandomValues(new Uint8Array(12));try{let n=await crypto.subtle.encrypt({name:`AES-GCM`,iv:c,additionalData:I(e,t,a)},o,s),l={kind:`browser_voiceid_device_v1`,walletId:e,walletAuthMethodId:t,privateKey:r.privateKey,publicKeyB64u:i,deviceKeyId:a,wrappingKey:o,nonce:c,ciphertext:n};return await f.setAppState(F(e,t),l),l}finally{s.fill(0)}}async function ce(e){let t=await crypto.subtle.decrypt({name:`AES-GCM`,iv:e.nonce,additionalData:I(e.walletId,e.walletAuthMethodId,e.deviceKeyId)},e.wrappingKey,e.ciphertext);return new Uint8Array(t)}const R=`voiceid-browser-enrollment-v1`,z=.2,B=[p.appState,p.walletSelections,p.walletAuthMethods,p.walletAuthorities];function V(e,t){return{walletId:e.walletId,walletAuthMethodId:e.walletAuthMethodId,deviceKeyId:e.deviceKeyId,authorityId:t}}async function H(e,t){let n=await f.getWalletAuthMethodV2(t),r=await L(e,t);if(!n||n.kind!==`voiceid`||n.status!==`active`||n.walletId!==e||!r||n.deviceKeyId!==r.deviceKeyId)throw Error(`VoiceID is unavailable for this wallet method.`);return V(r,n.walletAuthorityId)}function U(e){return`voiceid-browser-enrollment-v2:${[e.walletId,e.authorityId,e.walletAuthMethodId,e.deviceKeyId].map(encodeURIComponent).join(`:`)}`}function W(e){if(!Array.isArray(e)||!e.every(y))throw Error(`VoiceID template is invalid.`);let t=M(e);for(let n=0;n<e.length;n+=1)if(Math.abs(e[n]-t[n])>1e-6)throw Error(`VoiceID template is not normalized.`);return e.slice()}function G(e,t){if(e===void 0)return null;let n=w(e,`VoiceID enrollment`),r=w(n.scope,`VoiceID enrollment scope`);if(n.kind!==`browser_voiceid_enrollment_v2`||r.walletId!==t.walletId||r.authorityId!==t.authorityId||r.walletAuthMethodId!==t.walletAuthMethodId||r.deviceKeyId!==t.deviceKeyId||typeof n.generation!=`string`||!n.generation||typeof n.revision!=`number`||!Number.isSafeInteger(n.revision)||n.revision<0)throw Error(`VoiceID enrollment identity is invalid.`);if(n.model!==k)return null;let i=W(n.baseline),a={kind:`baseline`};try{let e=w(n.adaptation,`VoiceID improvements`);if(e.kind===`adapted`&&Array.isArray(e.samples)&&e.samples.length===3){let t=e.samples.map(W);q(i,t),a={kind:`adapted`,samples:t}}}catch{a={kind:`baseline`}}return{kind:`browser_voiceid_enrollment_v2`,scope:t,model:k,generation:n.generation,revision:n.revision,baseline:i,adaptation:a}}function K(e,t){let n=Array(192).fill(0);for(let e of t)for(let r=0;r<n.length;r+=1)n[r]+=e[r]/t.length;let r=M(n);for(let t=0;t<n.length;t+=1)n[t]=(1-z)*e[t]+z*r[t];return M(n)}function q(e,t){if(t.length!==3)throw Error(`Record three samples before saving voice improvements.`);for(let n of t)if(N(e,n)<.45)throw Error(`This sample differs from your original voice. Try again or re-record your enrollment.`);if(N(e,K(e,t))<.98)throw Error(`These samples differ too much from your original voice. Re-record your enrollment instead.`)}async function le(e){let t=(await f.seamsWalletDB.getDB()).transaction(p.appState,`readwrite`);try{let n=await t.store.get(U(e)),r=G(n?.value,e);return r&&JSON.stringify(n?.value?.adaptation)!==JSON.stringify(r.adaptation)&&(r=Z(r,r.adaptation),await t.store.put({key:U(e),value:r})),await t.done,r}catch(e){try{t.abort()}catch{}throw await t.done.catch(X),e}}async function ue(){let e=await f.getAppState(R);if(e===void 0)return null;let t=w(e,`Previous VoiceID enrollment`);return t.kind!==`browser_voiceid_enrollment_v1`||t.model!==k?null:W(t.template)}async function J(e,t,n){let r=ne(await e.objectStore(p.walletSelections).get(t.walletId)),i=te(await e.objectStore(p.walletAuthorities).get(t.authorityId)),a=await e.objectStore(p.walletAuthMethods).get(t.walletAuthMethodId),o=C(a);if(a!==void 0&&!o)throw Error(`VoiceID method storage is invalid.`);if(!i||i.record.state!==`active`||i.record.walletId!==t.walletId)throw Error(`VoiceID authority changed. Start again.`);if(o&&(o.record.kind!==`voiceid`||o.record.status!==`active`||o.record.walletId!==t.walletId||o.record.walletAuthorityId!==t.authorityId||o.record.deviceKeyId!==t.deviceKeyId)||(n===null||n.target===`enrolled`)&&!o)throw Error(`VoiceID method is unavailable.`);if((await e.objectStore(p.appState).get(F(t.walletId,t.walletAuthMethodId)))?.value?.deviceKeyId!==t.deviceKeyId)throw Error(`VoiceID device changed. Start again.`);let s=x((await e.objectStore(p.appState).get(h(t.walletId,t.walletAuthMethodId)))?.value);if(!s.enabled)throw Error(`VoiceID is turned off.`);if(n){if(!T(_({permissions:i.record.permissions}),_({permissions:v()})))throw Error(`Full owner authority is required to change VoiceID enrollment.`);let a=C(await e.objectStore(p.walletAuthMethods).get(n.sessionMethodId));if(n.expiresAtMs<=Date.now()||s.revision!==n.availabilityRevision||!r||r.record.lockState!==`unlocked`||r.record.lockGeneration!==n.lockGeneration||r.record.walletAuthMethodId!==n.sessionMethodId||!a||a.record.status!==`active`||n.target===`activating`&&a.record.kind===`voiceid`||a.record.walletId!==t.walletId||a.record.walletAuthorityId!==t.authorityId)throw Error(`The owner session changed or expired. Start again.`);if(n.target===`enrolled`){let r=C(await e.objectStore(p.walletAuthMethods).get(n.confirmationMethodId));if(!r||r.record.kind!==`passkey`||r.record.status!==`active`||r.record.walletId!==t.walletId||r.record.walletAuthorityId!==t.authorityId)throw Error(`The confirming passkey changed or was removed. Start again.`)}}}async function Y(e,t){let n=(await f.seamsWalletDB.getDB()).transaction(B,`readwrite`);try{await J(n,e,t),await n.done}catch(e){throw $(n),await n.done.catch(X),e}}function X(){}async function de(e,t){let n=e.scope,r=e.kind===`activation`?e.owner:null;if(r&&r.target!==`activating`)throw Error(`VoiceID activation requires its owner scope.`);let i=(await f.seamsWalletDB.getDB()).transaction(B,`readwrite`);try{await J(i,n,r);let e=i.objectStore(p.appState),a=await e.get(U(n)),o=G(a?.value,n);if(!o)throw Error(`Unlock with a passkey, then open Manage voice to import or record your enrollment.`);t.throwIfAborted();let s=o.adaptation,c=s.kind===`adapted`?K(o.baseline,s.samples):[...o.baseline];return JSON.stringify(a?.value?.adaptation)!==JSON.stringify(s)&&await e.put({key:U(n),value:Z(o,s)}),await i.done,t.throwIfAborted(),{template:c,baseline:[...o.baseline],kind:s.kind}}catch(e){try{i.abort()}catch{}throw await i.done.catch(X),e}}function Z(e,t){return{kind:`browser_voiceid_enrollment_v2`,scope:e.scope,model:e.model,generation:e.generation,revision:e.revision+1,baseline:e.baseline,adaptation:t}}async function Q(e,t,n,r,i){let a;if(r.kind===`activate`){if(t.target!==`activating`||n.kind!==`missing`)throw Error(`VoiceID activation cannot replace an enrollment.`)}else{if(t.target!==`enrolled`)throw Error(`VoiceID changes require a confirmation guard.`);await r.confirmation.consume(e,t,n,r.kind,i)}if(r.kind===`import`&&(t.target!==`enrolled`||n.kind!==`missing`))throw Error(`Import requires an existing method without a scoped enrollment.`);if(r.kind===`replace`||r.kind===`activate`||r.kind===`import`)a={kind:`browser_voiceid_enrollment_v2`,scope:e,model:k,generation:crypto.randomUUID(),revision:0,baseline:W(r.baseline),adaptation:{kind:`baseline`}};else{if(n.kind!==`present`)throw Error(`Record or import your original voice first.`);r.kind===`improve`?(q(n.enrollment.baseline,r.samples),a=Z(n.enrollment,{kind:`adapted`,samples:r.samples})):a=Z(n.enrollment,{kind:`baseline`})}if(new TextEncoder().encode(JSON.stringify(a)).length>32768)throw Error(`VoiceID enrollment exceeds its storage limit.`);let o=await f.seamsWalletDB.getDB();i.throwIfAborted();let s=o.transaction(B,`readwrite`),c=$.bind(null,s);i.addEventListener(`abort`,c,{once:!0});try{await J(s,e,t);let o=s.objectStore(p.appState),c=G((await o.get(U(e)))?.value,e);if(n.kind===`missing`?c!==null:!c||c.generation!==n.enrollment.generation||c.revision!==n.enrollment.revision)throw Error(`VoiceID enrollment changed in another operation. Start again.`);i.throwIfAborted(),await o.put({key:U(e),value:a}),r.kind===`import`&&await fe(s),await s.done}catch(e){throw c(),await s.done.catch(X),e}finally{i.removeEventListener(`abort`,c)}}function $(e){try{e.abort()}catch{}}async function fe(e){let t=e.objectStore(p.appState),n=await e.objectStore(p.walletAuthMethods).getAll();for(let e of n){if(!b(e)||e.kind!==`voiceid`)continue;let n=C(e);if(!n)return;let r=n.record;if(r.kind!==`voiceid`||r.status!==`active`)continue;let i={walletId:r.walletId,authorityId:r.walletAuthorityId,walletAuthMethodId:r.walletAuthMethodId,deviceKeyId:r.deviceKeyId};try{if(!G((await t.get(U(i)))?.value,i))return}catch{return}}await t.delete(R)}async function pe(e,t,n){let r=document.querySelector(`.seams-voiceid-activation-surface`);if(!(r instanceof HTMLElement))throw Error(`Open VoiceID activation in the Wallet.`);await P(r,t,n),t.throwIfAborted();let i=new me(r,e,t,n);try{await i.result}finally{i.dispose()}}var me=class{completion=new m;result=this.completion.promise;status;prompt;phrase=o(null);record;cancel;samples=[];phase=`ready`;get closed(){return this.phase===`closed`}controller=new AbortController;guardTimer;checkingGuard=!1;constructor(e,t,n,r){this.plan=t,this.signal=n,this.model=r,this.prompt=new l(document,`Enroll your voice in this browser`,this.phrase),this.prompt.title.textContent=`Read aloud · 0 of 3 accepted`,this.status=this.prompt.status,this.record=this.prompt.record,this.cancel=this.prompt.cancel,this.record.textContent=`Record sample 1`,this.record.addEventListener(`click`,this.capture),this.cancel.setAttribute(`aria-label`,`Cancel enrollment`),this.cancel.addEventListener(`click`,this.cancelled),this.prompt.ready(t.kind===`improve`?`Read naturally. Your original enrollment stays unchanged.`:`Read the full phrase at your normal pace.`),e.replaceChildren(this.prompt.element),n.addEventListener(`abort`,this.cancelled,{once:!0}),this.guardTimer=setInterval(this.checkGuard,250),n.aborted?this.cancelled():this.record.focus()}capture=()=>{this.collect()};async collect(){if(this.phase!==`ready`)return;let e=this.samples.length+1;try{c(),this.phase=`recording`,this.record.disabled=!0,this.status.textContent=`Opening microphone…`;let e=await t(this.controller.signal,this.prompt.capture);if(this.signal.aborted||this.closed){e.kind===`captured`&&e.samples.fill(0);return}if(e.kind===`insufficient_audio`){this.status.textContent=n(e.quality);return}if(this.plan.kind===`improve`&&e.quality.noise!==`normal`){e.samples.fill(0),this.status.textContent=`Use a quiet place to improve your voice enrollment.`;return}let a;try{if(await Y(this.plan.scope,this.plan.guard),this.status.textContent=`Checking your voice…`,(await i(e.samples,this.phrase,this.model)).kind===`mismatch`)throw new r;a=await j(e.samples)}finally{e.samples.fill(0)}if(this.signal.aborted||this.closed){a.fill(0);return}if(this.plan.kind===`improve`&&N(this.plan.snapshot.enrollment.baseline,a)<.45)throw a.fill(0),Error(`Your voice did not match the original enrollment. To replace it, return to Manage voice and choose Record voice again.`);let o=1;for(let e of this.samples)o=Math.min(o,N(e,a));this.status.dataset.voiceCapture=JSON.stringify({attemptId:e.quality.attemptId,capturedAtMs:e.quality.capturedAtMs,sample:this.samples.length+1,durationMs:e.quality.durationMs,recordingMs:e.quality.recordingMs,endedBy:e.quality.endedBy,voicedMs:e.quality.voicedMs,microphone:e.quality.microphone,microphoneSelection:e.quality.microphoneSelection,processing:e.quality.processing,backgroundRms:e.quality.backgroundRms,speechToBackgroundDb:e.quality.speechToBackgroundDb,noise:e.quality.noise,minimumSimilarity:o});for(let e of this.samples)if(N(e,a)<.45)throw a.fill(0),Error(`This sample differs from the earlier samples. Use the same microphone and try again.`);if(this.samples.push(a),this.samples.length<3){this.prompt.accepted(),this.status.textContent=`Sample ${this.samples.length} accepted. Record the next sample.`,this.record.textContent=`Record sample ${this.samples.length+1}`;return}let s=Array(192).fill(0);for(let e of this.samples)for(let t=0;t<s.length;t+=1)s[t]+=e[t]/3;this.phase=`committing`,this.prompt.title.textContent=`3 of 3 accepted`,this.status.textContent=`Saving voice enrollment…`;let l=this.plan;switch(l.kind){case`activate`:await Q(l.scope,l.guard,l.snapshot,{kind:`activate`,baseline:M(s)},this.controller.signal);break;case`replace`:await Q(l.scope,l.guard,l.snapshot,{kind:`replace`,baseline:M(s),confirmation:l.confirmation},this.controller.signal);break;case`improve`:await Q(l.scope,l.guard,l.snapshot,{kind:`improve`,samples:this.samples,confirmation:l.confirmation},this.controller.signal);break}if(this.signal.aborted||this.closed)return;this.phase=`closed`,this.prompt.accepted(),this.status.textContent=`Voice enrollment saved.`,this.completion.resolve()}catch(e){if(!this.closed&&this.phase===`committing`){this.phase=`closed`,this.controller.abort(),this.completion.reject(D(e));return}!this.closed&&this.samples.length===3&&this.samples.pop()?.fill(0),this.closed||(this.status.textContent=e instanceof Error?e.message:`VoiceID enrollment failed.`)}finally{this.closed||(this.samples.length<e&&(this.record.textContent=`Retry sample ${e}`,this.status.textContent=`Sample ${e} was not accepted. ${this.status.textContent??``}`),this.phrase=o(this.phrase),this.prompt.setPhrase(this.phrase),this.prompt.title.textContent=`Read aloud · ${this.samples.length} of 3 accepted`,this.prompt.ready(this.status.textContent??``),this.phase=`ready`,this.record.disabled=!1)}}cancelled=()=>{this.closed||(this.phase=`closed`,this.controller.abort(),this.completion.reject(Error(`VoiceID enrollment was cancelled.`)))};checkGuard=()=>{this.closed||this.checkingGuard||(this.checkingGuard=!0,this.validateGuard())};async validateGuard(){try{await Y(this.plan.scope,this.plan.guard)}catch(e){this.closed||(this.phase=`closed`,this.controller.abort(),this.completion.reject(D(e)))}finally{this.checkingGuard=!1}}dispose(){this.phase=`closed`,this.controller.abort(),clearInterval(this.guardTimer),this.signal.removeEventListener(`abort`,this.cancelled),this.prompt.dispose(),this.record.removeEventListener(`click`,this.capture),this.cancel.removeEventListener(`click`,this.cancelled);for(let e of this.samples)e.fill(0)}};async function he(e,n,r,a,o){delete document.body.dataset.voiceVerification;let s=await t(n,r);if(s.kind===`insufficient_audio`)return n.throwIfAborted(),s;try{n.throwIfAborted();let t=await i(s.samples,a,o),r=t.kind===`matched`,c=s.samples.length/16,l=await j(s.samples),u,d,f;try{n.throwIfAborted();let t=await de(e,n);try{u=N(l,t.template),d=N(l,t.baseline),f=t.kind}finally{t.template.fill(0),t.baseline.fill(0)}}finally{l.fill(0)}return document.body.dataset.voiceVerification=JSON.stringify({attemptId:s.quality.attemptId,capturedAtMs:s.quality.capturedAtMs,durationMs:s.quality.durationMs,recordingMs:s.quality.recordingMs,endedBy:s.quality.endedBy,voicedMs:s.quality.voicedMs,microphone:s.quality.microphone,microphoneSelection:s.quality.microphoneSelection,processing:s.quality.processing,backgroundRms:s.quality.backgroundRms,speechToBackgroundDb:s.quality.speechToBackgroundDb,noise:s.quality.noise,similarity:u,baselineSimilarity:d,referenceKind:f,modelInputMs:c,threshold:.45,phraseMatched:r,phraseRecognition:t.recognition,expectedPhrase:a}),u<.45?{kind:`mismatch`,quality:s.quality}:t.kind===`matched`?{kind:`matched`}:{kind:`phrase_mismatch`}}finally{s.samples.fill(0)}}export{ue as a,V as c,L as d,P as f,Q as i,oe as l,he as n,le as o,Y as r,H as s,pe as t,ce as u};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./prompt-vztg41jG.js";import{a as e,n as t}from"./deviceBridge-DQLPNdC8.js";import{f as n,o as r,s as i}from"./enrollment-DPOYb36Z.js";import{IndexedDBManager as a,fetchWithWalletHomeDiscovery as o,readAuthMethodEnabled as s}from"../voiceid-host.js";async function c(e){let{authMethod:t,challenge:n}=e;if(n.walletId!==t.walletId||n.walletAuthMethodId!==t.walletAuthMethodId||n.walletAuthorityId!==t.walletAuthorityId||n.deviceKeyId!==t.deviceKeyId)throw Error(`VoiceID challenge does not match the selected device method.`);let r=await e.capture({challenge:n,signal:e.signal,onCapture:e.onCapture,phrase:e.phrase});try{if(e.signal.aborted)throw Error(`VoiceID unlock was cancelled.`);let n=await e.http.verify({assertion:r.assertion,signal:e.signal});if(e.signal.aborted)throw Error(`VoiceID unlock was cancelled.`);if(n.walletSession.walletId!==t.walletId||n.walletSession.authMethodId!==t.walletAuthMethodId||n.walletSession.authorityId!==t.walletAuthorityId||n.custodyEnvelope.factor.deviceKeyId!==t.deviceKeyId)throw Error(`VoiceID server verified a different device method.`);return await e.install(n,r.factorSecret32)}finally{r.factorSecret32.fill(0)}}async function l(t,c,l,u){if(!await s(t.walletId,t.walletAuthMethodId))throw Error(`VoiceID is turned off in this browser. Enable it in Authentication methods.`);let f=await t.prepareSession(c),p=await a.getWalletAuthMethodV2(t.walletAuthMethodId);if(!p||p.kind!==`voiceid`||p.status!==`active`||p.walletId!==t.walletId||p.walletAuthMethodId!==t.walletAuthMethodId)throw Error(`Activate VoiceID on this device before unlocking this wallet.`);let m=new e(t.relayerUrl,location.origin,o);if(!await r(await i(t.walletId,t.walletAuthMethodId)))throw Error(`Unlock with a passkey, then open Manage voice to import or record your enrollment.`);await n(l,c,u),c.throwIfAborted(),t.assertActive();let h=await m.challenge({walletId:t.walletId,walletAuthMethodId:t.walletAuthMethodId,signal:AbortSignal.any([c,AbortSignal.timeout(15e3)])});t.assertActive();let g={authMethod:p,challenge:h,http:m,install:f};return c.throwIfAborted(),{kind:`recording`,expiresAtMs:h.expiresAtMs,action:d.bind(null,u,g)}}function u(e,t,n,r){return e({walletSession:n.walletSession,operationCredential:n.operationCredential,custodyEnvelope:n.custodyEnvelope,factorSecret32:r,signal:t,near:n.near})}async function d(e,n,r,i,a){return await c({authMethod:n.authMethod,challenge:n.challenge,http:n.http,capture:t.bind(null,e),install:u.bind(null,n.install,r),signal:r,onCapture:i,phrase:a})}export{l as prepareUnlockSurface};
|