@robota-sdk/agent-transport 3.0.0-beta.94 → 3.0.0-beta.95

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +2 -1
  3. package/dist/browser/client.d.ts +1 -1
  4. package/dist/browser/client.js +1 -1
  5. package/dist/browser/index.d.ts +34 -4
  6. package/dist/browser/index.d.ts.map +1 -1
  7. package/dist/browser/index.js +1 -1
  8. package/dist/browser/index.js.map +1 -1
  9. package/dist/browser/{observer-messages-Dnff2Tdb.d.ts → observer-messages-DoWb6RNv.d.ts} +23 -1
  10. package/dist/browser/observer-messages-DoWb6RNv.d.ts.map +1 -0
  11. package/dist/browser/observer-messages-UG77sTYK.js +2 -0
  12. package/dist/browser/observer-messages-UG77sTYK.js.map +1 -0
  13. package/dist/node/index.cjs +1 -1
  14. package/dist/node/index.d.cts +54 -3
  15. package/dist/node/index.d.cts.map +1 -1
  16. package/dist/node/index.d.ts +54 -3
  17. package/dist/node/index.d.ts.map +1 -1
  18. package/dist/node/index.js +1 -1
  19. package/dist/node/index.js.map +1 -1
  20. package/dist/node/node.cjs +1 -1
  21. package/dist/node/node.d.cts +19 -1
  22. package/dist/node/node.d.cts.map +1 -1
  23. package/dist/node/node.d.ts +19 -1
  24. package/dist/node/node.d.ts.map +1 -1
  25. package/dist/node/node.js +1 -1
  26. package/dist/node/node.js.map +1 -1
  27. package/package.json +8 -8
  28. package/src/__tests__/app-tool-messages.test.ts +463 -0
  29. package/src/__tests__/message-decoders.test.ts +101 -0
  30. package/src/__tests__/permission-summary.test.ts +18 -0
  31. package/src/__tests__/session-message-handler-observe.test.ts +6 -0
  32. package/src/__tests__/session-message-handler.test.ts +3 -2
  33. package/src/__tests__/session-resume-bridge.test.ts +66 -0
  34. package/src/__tests__/stdio-carrier.test.ts +85 -0
  35. package/src/app-tool-messages.ts +140 -0
  36. package/src/index.ts +7 -0
  37. package/src/message-decoders.ts +16 -0
  38. package/src/node/index.ts +2 -0
  39. package/src/node/stdio-carrier.ts +59 -0
  40. package/src/permission-summary.ts +18 -0
  41. package/src/protocol-session.ts +7 -1
  42. package/src/session-events.ts +1 -0
  43. package/src/session-message-handler.ts +37 -1
  44. package/src/session-resume-bridge.ts +15 -0
  45. package/src/wire-messages.ts +35 -0
  46. package/dist/browser/observer-messages-CEeisRaF.js +0 -2
  47. package/dist/browser/observer-messages-CEeisRaF.js.map +0 -1
  48. package/dist/browser/observer-messages-Dnff2Tdb.d.ts.map +0 -1
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("node:crypto"),t=require("jose"),n=require("@robota-sdk/agent-core/node"),r=require("node:net");function i(){let t=(0,e.randomBytes)(32).toString(`hex`);if(t.length!==64)throw Error(`transport admission: could not mint a credential of the required length`);return t}function a(e={}){let t=`open`in e&&e.open===!0,n=e.token!==void 0&&e.token!==null&&e.token!==``;if(n&&t)throw Error("transport admission: a token and `open: true` are contradictory — one requires a credential and the other requires none. Asking for both means one of the two beliefs behind the call is wrong; say which is intended.");if(n)return{token:e.token};if(e.token===null||t){let t=e.openReason?.trim();if(!t)throw Error("transport admission: an open transport requires `openReason`. An open transport is a decision, and a decision nobody wrote down is indistinguishable from an oversight.");return{token:null,openReason:t}}return{token:i()}}function o(t,n){if(n===void 0)return!1;let r=Buffer.from(t,`utf8`),i=Buffer.from(n,`utf8`);return r.length===i.length&&(0,e.timingSafeEqual)(r,i)}function s(e){if(!e||e.slice(0,6).toLowerCase()!==`bearer`)return;let t=e.slice(6);if(t===``||t[0]!==` `&&t[0]!==` `)return;let n=t.replace(/^[ \t]+/,``);return n===``?void 0:n}function c(t){let n=JSON.stringify(t),r=Buffer.from(n,`utf8`);return{serialized:n,integrity:{digest:(0,e.createHash)(`sha256`).update(r).digest(`base64url`),byteLength:r.byteLength}}}function l(t,n){let r=Buffer.from(t,`utf8`);return r.byteLength===n.byteLength?(0,e.createHash)(`sha256`).update(r).digest(`base64url`)===n.digest?{intact:!0}:{intact:!1,failure:`digest-mismatch`}:{intact:!1,failure:`truncated`,expectedBytes:n.byteLength,actualBytes:r.byteLength}}const u=10*6e4,d=`at+jwt`,f=[`RS256`,`ES256`,`EdDSA`],p={RS256:{kty:`RSA`},ES256:{kty:`EC`,crv:`P-256`},EdDSA:{kty:`OKP`,crv:`Ed25519`}};var m=class{refusal;constructor(e){this.refusal=e}};function h(e,r={}){let i=g(e),a=r.now??Date.now,o=r.monotonicNow??(()=>performance.now()),s={fetch:r.fetch,lookup:r.lookup},c=i.hostname.replace(/^\[|\]$/g,``).toLowerCase(),l={keys:void 0,jwksUri:void 0,fetchedAt:void 0,lastAttemptAt:void 0,inflight:void 0},f=new Set(e.allowedSubjects??[]),p=new Set(e.allowedClients??[]);async function h(e){if(new URL(e).protocol!==`https:`)return;let t=await(0,n.fetchWithEgressPolicy)(e,{headers:{accept:`application/json`},timeoutMs:1e4,maxResponseBytes:65536},{allowedHosts:[c],maxRedirects:0},s);if(!(!t.ok||t.status!==200))return JSON.parse(new TextDecoder().decode(t.body))}async function C(){for(let t of _(i)){let n=await h(t).catch(()=>void 0);if(!S(n))continue;if(n.issuer!==e.issuer)return;let r=n.jwks_uri;return typeof r!=`string`||!URL.canParse(r)?void 0:new URL(r).protocol===`https:`?r:void 0}}function w(){return l.fetchedAt===void 0?1/0:o()-l.fetchedAt}async function T(){l.lastAttemptAt=o();try{if(w()>=u&&(l.jwksUri=void 0),l.jwksUri??=await C(),l.jwksUri===void 0)return;let e=v(await h(l.jwksUri));if(e===void 0)return;l.keys=e,l.fetchedAt=o()}catch{}}async function E(){if(l.inflight!==void 0)return l.inflight;if(!(l.lastAttemptAt!==void 0&&o()-l.lastAttemptAt<3e4))return l.inflight=T().finally(()=>{l.inflight=void 0}),l.inflight}async function D(){return w()>=u&&await E(),w()>=36e5&&(l.keys=void 0,l.jwksUri=void 0,l.fetchedAt=void 0),l.keys}async function O(e,t){let n=await D();if(n===void 0)throw new m(`keys-unavailable`);if(e===void 0){if(n.length>1)throw new m(`ambiguous-key`);let e=n[0];if(e===void 0)throw new m(`unknown-key`);if(!y(e,t))throw new m(`key-mismatch`);return e}let r=n.filter(t=>t.kid===e);if(r.length===0&&(await E(),n=l.keys??[],r=n.filter(t=>t.kid===e)),r.length===0)throw new m(`unknown-key`);let i=r.find(e=>y(e,t));if(i===void 0)throw new m(`key-mismatch`);return i}async function k(n){if(typeof n!=`string`||n.length===0)throw new m(`malformed`);if(n.length>8192)throw new m(`oversize`);let r;try{r=(0,t.decodeProtectedHeader)(n)}catch{throw new m(`malformed`)}if(typeof r.typ!=`string`||x(r.typ)!==d)throw new m(`wrong-type`);let i=e.algorithms.find(e=>e===r.alg);if(i===void 0)throw new m(`unsupported-algorithm`);if(r.kid!==void 0&&typeof r.kid!=`string`)throw new m(`malformed`);let o=await O(r.kid,i),s;try{s=await(0,t.importJWK)(o,i)}catch{throw new m(`key-mismatch`)}let c;try{({payload:c}=await(0,t.jwtVerify)(n,s,{issuer:e.issuer,audience:e.resource,algorithms:[i],typ:d,clockTolerance:60,currentDate:new Date(a()),requiredClaims:[`exp`,`sub`,`client_id`]}))}catch(e){throw new m(b(e))}let l=typeof c.scope==`string`?c.scope.split(` `):[];if(!e.requiredScopes.every(e=>l.includes(e)))throw new m(`missing-scope`);let u=c.sub,h=c.client_id;if(!(typeof u==`string`&&f.has(u)||typeof h==`string`&&p.has(h)))throw new m(`principal-not-allowed`);return{admitted:!0}}return{async verify(e){try{return await k(e)}catch(e){return{admitted:!1,refusal:e instanceof m?e.refusal:`malformed`}}}}}function g(e){if(!URL.canParse(e.issuer))throw Error(`access-token verifier: issuer is not a URL`);let t=new URL(e.issuer);if(t.protocol!==`https:`||t.search!==``||t.hash!==``)throw Error(`access-token verifier: issuer must be an https URL with no query or fragment`);if(!URL.canParse(e.resource))throw Error(`access-token verifier: resource is not a URL`);if(e.algorithms.length===0||!e.algorithms.every(e=>f.includes(e)))throw Error(`access-token verifier: algorithms must be a non-empty subset of RS256, ES256, EdDSA`);if(e.requiredScopes.length===0)throw Error(`access-token verifier: at least one required scope is needed`);if((e.allowedSubjects?.length??0)+(e.allowedClients?.length??0)===0)throw Error(`access-token verifier: name at least one allowed subject or client — this host is single-tenant, and issuer + audience + scope alone admit anyone the issuer serves`);return t}function _(e){let t=e.pathname===`/`?``:e.pathname.replace(/\/$/,``);return[`${e.origin}/.well-known/oauth-authorization-server${t}`,`${e.origin}${t}/.well-known/openid-configuration`]}function v(e){if(!(!S(e)||!Array.isArray(e.keys)||e.keys.length>32))return e.keys.filter(e=>S(e)&&typeof e.kty==`string`)}function y(e,t){let n=p[t];return e.kty!==n.kty||n.crv!==void 0&&e.crv!==n.crv||e.alg!==void 0&&e.alg!==t||e.use!==void 0&&e.use!==`sig`||e.key_ops!==void 0&&!e.key_ops.includes(`verify`)?!1:e.d===void 0}function b(e){let t=S(e)?e.code:void 0,n=S(e)?e.claim:void 0;switch(t){case`ERR_JWT_EXPIRED`:return`expired`;case`ERR_JWS_SIGNATURE_VERIFICATION_FAILED`:return`bad-signature`;case`ERR_JOSE_ALG_NOT_ALLOWED`:return`unsupported-algorithm`;case`ERR_JWT_CLAIM_VALIDATION_FAILED`:return n===`iss`?`wrong-issuer`:n===`aud`?`wrong-audience`:n===`nbf`?`not-yet-valid`:n===`typ`?`wrong-type`:`malformed`;default:return`malformed`}}function x(e){let t=e.toLowerCase();return t.startsWith(`application/`)?t.slice(12):t}function S(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}const C=6e4,w=4096,T=/^[\x21\x23-\x5b\x5d-\x7e]+$/,E=new r.BlockList;E.addSubnet(`127.0.0.0`,8,`ipv4`),E.addAddress(`::1`,`ipv6`);const D=new r.BlockList;D.addSubnet(`10.0.0.0`,8,`ipv4`),D.addSubnet(`172.16.0.0`,12,`ipv4`),D.addSubnet(`192.168.0.0`,16,`ipv4`),D.addSubnet(`169.254.0.0`,16,`ipv4`),D.addSubnet(`100.64.0.0`,10,`ipv4`),D.addSubnet(`fc00::`,7,`ipv6`),D.addSubnet(`fe80::`,10,`ipv6`);function O(e){if(e===void 0)return;let t=e.trim().toLowerCase(),n=t.startsWith(`::ffff:`)?t.slice(7):void 0;return n!==void 0&&(0,r.isIP)(n)===4?n:(0,r.isIP)(t)===0?void 0:t}function k(e){if(e===void 0)return`unknown`;let t=(0,r.isIP)(e)===4?`ipv4`:`ipv6`;return E.check(e,t)?`loopback`:D.check(e,t)?`private`:`public`}function A(e,t){let n;try{n=new URL(e)}catch{throw Error(`${t} public URL must be an absolute https URL`)}if(n.protocol!==`https:`)throw Error(`${t} public URL must use https`);if(n.username!==``||n.password!==``||n.search!==``||n.hash!==``)throw Error(`${t} public URL must not carry credentials, a query or a fragment`);return n}function j(e){let t=new Map;return n=>{let r=e(),i=t.get(n);if(i===void 0||r-i.startedAt>=C){if(t.delete(n),t.size>=w){for(let[e,n]of t)r-n.startedAt>=C&&t.delete(e);let e=t.keys().next();t.size>=w&&e.done!==!0&&t.delete(e.value)}i={count:0,startedAt:r},t.set(n,i)}i.count+=1;let a=C-(r-i.startedAt);return{throttled:i.count>20,retryAfterSeconds:Math.max(1,Math.ceil(a/1e3))}}}function M(e){let t=A(e.publicUrl,e.label),n=new Set;for(let t of e.trustedProxies??[]){let r=O(t);if(r===void 0)throw Error(`${e.label} trusted proxy must be a literal IP address`);n.add(r)}let r=new Set([t.host]);t.port===``&&r.add(`${t.hostname}:443`);let i=j(e.now??(()=>performance.now()));function a(e){let t=O(e.socket.remoteAddress);if(t===void 0||!n.has(t))return t;let r=e.headers[`x-forwarded-for`],i=(Array.isArray(r)?r.join(`,`):r??``).split(`,`);for(let e=i.length-1;e>=0;--e){let r=O(i[e]);if(r===void 0)return t;if(!n.has(r))return r}return t}return{url:t,checkNames(e,n){let i=e.headers.host?.toLowerCase(),a=e.headers.origin;return i===void 0||!r.has(i)||a!==void 0&&a!==t.origin?(n.writeHead(403).end(),!1):!0},fail(e){let t=a(e);return{remote:k(t),...i(t??`unknown`)}},remote(e){return k(a(e))}}}function N(e){let t=A(e.resource,e.label),n;try{n=new URL(e.issuer)}catch{throw Error(`${e.label} authorization issuer must be an absolute https URL`)}if(n.protocol!==`https:`)throw Error(`${e.label} authorization issuer must use https`);if(e.scopes.length===0||!e.scopes.every(e=>T.test(e)))throw Error(`${e.label} authorization needs at least one valid scope`);let r=`/.well-known/oauth-protected-resource${t.pathname===`/`?``:t.pathname}`,i=`${t.origin}${r}`,a=e.scopes.join(` `);return{wellKnownPath:r,metadata:JSON.stringify({resource:e.resource,authorization_servers:[e.issuer],scopes_supported:[...e.scopes],bearer_methods_supported:[`header`]}),challenges:{missing:`Bearer resource_metadata="${i}"`,invalid:`Bearer error="invalid_token", resource_metadata="${i}"`,scope:`Bearer error="insufficient_scope", scope="${a}", resource_metadata="${i}"`}}}function P(e,t,n){if(e.method!==`GET`){t.writeHead(405,{Allow:`GET`}).end();return}t.writeHead(200,{"Content-Type":`application/json`}).end(n.metadata)}function F(e,t,n,r){if(r.throttled)e.writeHead(429,{"Retry-After":String(r.retryAfterSeconds)}).end();else if(n===`missing-scope`)e.writeHead(403,{"WWW-Authenticate":t.challenges.scope}).end();else{let r=n===`missing-token`?t.challenges.missing:t.challenges.invalid;e.writeHead(401,{"WWW-Authenticate":r}).end()}}function I(e,t,n,r,i,a){if(i===`keys-unavailable`){a?.({refusal:i,remote:n.remote(e),throttled:!1}),t.writeHead(503).end();return}let o=n.fail(e);a?.({refusal:i,remote:o.remote,throttled:o.throttled}),F(t,r,i===`missing-token`||i===`missing-scope`?i:`invalid-token`,o)}const L=/^[0-9a-f]{64}$/,R=/^[A-Za-z0-9+/]*={0,2}$/,z=3e4,B=new Set([`declined`,`too-large`,`bad-name`,`exists`,`unsafe-path`,`integrity`,`protocol`,`closed`,`timeout`,`unavailable`]),V=[[0,31],[127,159],[8203,8207],[8232,8238],[8294,8297],[65279,65279]];function H(e){let t=``;for(let n of e){let e=n.codePointAt(0)??0;t+=V.some(([t,n])=>e>=t&&e<=n)?` `:n}return t}function U(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=0}function W(e){if(e.length>22360)return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!=`object`||!t)return;let n=t;switch(n.t){case`file-offer`:{let{transferId:e,name:t,size:r,sha256:i}=n;return typeof e!=`string`||e.length===0||e.length>128||typeof t!=`string`||t.length>1024||!U(r)||typeof i!=`string`||!L.test(i)?void 0:{t:`file-offer`,transferId:e,name:t,size:r,sha256:i}}case`file-accept`:return{t:`file-accept`};case`file-refuse`:{let e=n.reason,t=n.detail;return typeof e!=`string`||!B.has(e)?void 0:{t:`file-refuse`,reason:e,...typeof t==`string`?{detail:H(t).slice(0,500)}:{}}}case`file-chunk`:{let{seq:e,data:t}=n;return!U(e)||typeof t!=`string`||t===``||!R.test(t)?void 0:{t:`file-chunk`,seq:e,data:t}}case`file-credit`:return U(n.upTo)?{t:`file-credit`,upTo:n.upTo}:void 0;case`file-end`:return U(n.chunks)?{t:`file-end`,chunks:n.chunks}:void 0;case`file-done`:return{t:`file-done`};default:return}}var G=class extends Error{reason;detail;constructor(e,t){super(t??e),this.reason=e,this.detail=t}},K=class{capacity;queue=[];waiter;failure;stops=[];constructor(e,t){this.capacity=t,this.stops.push(e.onFrame(e=>{let t=W(e);if(t===void 0){this.fail(new G(`protocol`,`a frame could not be read`));return}if(this.waiter!==void 0){let{resolve:e}=this.waiter;this.waiter=void 0,e(t);return}if(this.queue.length>=this.capacity){this.fail(new G(`protocol`,`the peer sent past its credit`));return}this.queue.push(t)}),e.onClose(()=>this.fail(new G(`closed`,`the channel closed`))))}fail(e){if(this.failure!==void 0)return;this.failure=e,e.reason===`protocol`&&(this.queue.length=0);let t=this.waiter;this.waiter=void 0,t?.reject(e)}next(e){let t=this.queue.shift();return t===void 0?this.failure===void 0?new Promise((t,n)=>{let r=setTimeout(()=>{this.waiter=void 0,n(new G(`timeout`,`nothing arrived within ${e}ms`))},e);r.unref?.(),this.waiter={resolve:e=>{clearTimeout(r),t(e)},reject:e=>{clearTimeout(r),n(e)}}}):Promise.reject(this.failure):Promise.resolve(t)}get ended(){return this.failure!==void 0}stop(){for(let e of this.stops.splice(0))e()}};function q(e,t){try{e.send(JSON.stringify(t))}catch(e){throw new G(`closed`,e instanceof Error?e.message:String(e))}}function J(e){return e instanceof G?e:new G(`unavailable`,e instanceof Error?e.message:String(e))}async function*Y(e,t){let n=Buffer.alloc(0);for await(let r of e)for(n=n.length===0?Buffer.from(r):Buffer.concat([n,r]);n.length>=t;)yield n.subarray(0,t),n=n.subarray(t);n.length>0&&(yield n)}async function X(e){let{channel:t,offer:n,source:r}=e,i=e.chunkBytes??16384,a=e.window??16,o=e.idleMs??z,s=new K(t,a+2);try{if(r.size!==n.size)throw new G(`integrity`,`the source is not the size offered`);q(t,{t:`file-offer`,...n});let c=await s.next(e.decisionMs??3e5);if(c.t===`file-refuse`)throw new G(c.reason,c.detail);if(c.t!==`file-accept`)throw new G(`protocol`,`expected an answer`);let l=0,u=0,d=0,f=async()=>{let e=await s.next(o);if(e.t===`file-refuse`)throw new G(e.reason,e.detail);if(e.t!==`file-credit`||e.upTo<=u||e.upTo>l)throw new G(`protocol`,`expected credit`);u=e.upTo};for await(let e of Y(r.read(),i)){if(d+=e.length,d>n.size)throw new G(`integrity`,`the source grew while sending`);for(;l-u>=a;)await f();q(t,{t:`file-chunk`,seq:l,data:e.toString(`base64`)}),l+=1}if(d!==n.size)throw new G(`integrity`,`the source shrank while sending`);for(q(t,{t:`file-end`,chunks:l});;){let e=await s.next(o);if(e.t===`file-done`)return{ok:!0};if(e.t===`file-refuse`)throw new G(e.reason,e.detail);if(e.t!==`file-credit`||e.upTo<=u||e.upTo>l)throw new G(`protocol`,`expected credit or completion`);u=e.upTo}}catch(e){let t=J(e);return{ok:!1,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail}}}finally{s.stop(),t.close()}}async function Z(t){let{channel:n}=t,r=t.maxBytes??33554432,i=t.window??16,a=t.idleMs??z,o=new K(n,i+1),s=new AbortController,c=n.onClose(()=>s.abort()),l,u,d=!0;try{let i=await o.next(a);if(i.t!==`file-offer`)throw new G(`protocol`,`expected an offer`);if(l={transferId:i.transferId,name:i.name,size:i.size,sha256:i.sha256},l.size>r)throw new G(`too-large`,`${l.size} bytes is over the ${r}-byte limit`);let c=await t.admit(l,s.signal);if(`refused`in c)throw new G(c.refused,c.detail);if(u=c.sink,o.ended)throw new G(`closed`,`the sender left before the answer`);q(n,{t:`file-accept`});let d=(0,e.createHash)(`sha256`),f=0,p=0;for(;;){let e=await o.next(a);if(e.t===`file-end`){if(e.chunks!==f||p!==l.size)throw new G(`integrity`,`the content is not the size offered`);if(d.digest(`hex`)!==l.sha256)throw new G(`integrity`,`the content does not match the offered sha256`);let t=await u.commit();return u=void 0,q(n,{t:`file-done`}),{ok:!0,offer:l,location:t}}if(e.t!==`file-chunk`||e.seq!==f)throw new G(`protocol`,`expected the next chunk`);let t=Buffer.from(e.data,`base64`);if(p+=t.length,p>l.size)throw new G(`integrity`,`more content than offered`);d.update(t),await u.write(t),f+=1,q(n,{t:`file-credit`,upTo:f})}}catch(e){let t=J(e);if(d=t.reason!==`protocol`,u!==void 0&&await u.discard().catch(()=>void 0),t.reason!==`closed`&&t.reason!==`timeout`)try{q(n,{t:`file-refuse`,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail}})}catch{}return{ok:!1,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail},...l===void 0?{}:{offer:l}}}finally{d&&await Q(s.signal,5e3),c(),o.stop(),n.close()}}function Q(e,t){return e.aborted?Promise.resolve():new Promise(n=>{let r=setTimeout(i,t);r.unref?.(),e.addEventListener(`abort`,i,{once:!0});function i(){clearTimeout(r),e.removeEventListener(`abort`,i),n()}})}exports.DEFAULT_MAX_FILE_BYTES=33554432,exports.FILE_CHUNK_BYTES=16384,exports.FILE_CREDIT_WINDOW=16,exports.bearerCredential=s,exports.createAccessTokenVerifier=h,exports.createBearerResourceServer=M,exports.credentialMatches=o,exports.describeProtectedResource=N,exports.mintTransportToken=i,exports.parsePublicHttpsUrl=A,exports.receiveFileOverChannel=Z,exports.refuseAccessToken=I,exports.refuseBearerToken=F,exports.resolveAdmission=a,exports.sealHandoffRecord=c,exports.sendFileOverChannel=X,exports.serveProtectedResourceMetadata=P,exports.verifyHandoffPayload=l;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("node:crypto"),t=require("jose"),n=require("@robota-sdk/agent-core/node"),r=require("node:net"),i=require("node:stream");function a(){let t=(0,e.randomBytes)(32).toString(`hex`);if(t.length!==64)throw Error(`transport admission: could not mint a credential of the required length`);return t}function o(e={}){let t=`open`in e&&e.open===!0,n=e.token!==void 0&&e.token!==null&&e.token!==``;if(n&&t)throw Error("transport admission: a token and `open: true` are contradictory — one requires a credential and the other requires none. Asking for both means one of the two beliefs behind the call is wrong; say which is intended.");if(n)return{token:e.token};if(e.token===null||t){let t=e.openReason?.trim();if(!t)throw Error("transport admission: an open transport requires `openReason`. An open transport is a decision, and a decision nobody wrote down is indistinguishable from an oversight.");return{token:null,openReason:t}}return{token:a()}}function s(t,n){if(n===void 0)return!1;let r=Buffer.from(t,`utf8`),i=Buffer.from(n,`utf8`);return r.length===i.length&&(0,e.timingSafeEqual)(r,i)}function c(e){if(!e||e.slice(0,6).toLowerCase()!==`bearer`)return;let t=e.slice(6);if(t===``||t[0]!==` `&&t[0]!==` `)return;let n=t.replace(/^[ \t]+/,``);return n===``?void 0:n}function l(t){let n=JSON.stringify(t),r=Buffer.from(n,`utf8`);return{serialized:n,integrity:{digest:(0,e.createHash)(`sha256`).update(r).digest(`base64url`),byteLength:r.byteLength}}}function u(t,n){let r=Buffer.from(t,`utf8`);return r.byteLength===n.byteLength?(0,e.createHash)(`sha256`).update(r).digest(`base64url`)===n.digest?{intact:!0}:{intact:!1,failure:`digest-mismatch`}:{intact:!1,failure:`truncated`,expectedBytes:n.byteLength,actualBytes:r.byteLength}}const d=10*6e4,f=`at+jwt`,p=[`RS256`,`ES256`,`EdDSA`],m={RS256:{kty:`RSA`},ES256:{kty:`EC`,crv:`P-256`},EdDSA:{kty:`OKP`,crv:`Ed25519`}};var h=class{refusal;constructor(e){this.refusal=e}};function g(e,r={}){let i=_(e),a=r.now??Date.now,o=r.monotonicNow??(()=>performance.now()),s={fetch:r.fetch,lookup:r.lookup},c=i.hostname.replace(/^\[|\]$/g,``).toLowerCase(),l={keys:void 0,jwksUri:void 0,fetchedAt:void 0,lastAttemptAt:void 0,inflight:void 0},u=new Set(e.allowedSubjects??[]),p=new Set(e.allowedClients??[]);async function m(e){if(new URL(e).protocol!==`https:`)return;let t=await(0,n.fetchWithEgressPolicy)(e,{headers:{accept:`application/json`},timeoutMs:1e4,maxResponseBytes:65536},{allowedHosts:[c],maxRedirects:0},s);if(!(!t.ok||t.status!==200))return JSON.parse(new TextDecoder().decode(t.body))}async function g(){for(let t of v(i)){let n=await m(t).catch(()=>void 0);if(!C(n))continue;if(n.issuer!==e.issuer)return;let r=n.jwks_uri;return typeof r!=`string`||!URL.canParse(r)?void 0:new URL(r).protocol===`https:`?r:void 0}}function w(){return l.fetchedAt===void 0?1/0:o()-l.fetchedAt}async function T(){l.lastAttemptAt=o();try{if(w()>=d&&(l.jwksUri=void 0),l.jwksUri??=await g(),l.jwksUri===void 0)return;let e=y(await m(l.jwksUri));if(e===void 0)return;l.keys=e,l.fetchedAt=o()}catch{}}async function E(){if(l.inflight!==void 0)return l.inflight;if(!(l.lastAttemptAt!==void 0&&o()-l.lastAttemptAt<3e4))return l.inflight=T().finally(()=>{l.inflight=void 0}),l.inflight}async function D(){return w()>=d&&await E(),w()>=36e5&&(l.keys=void 0,l.jwksUri=void 0,l.fetchedAt=void 0),l.keys}async function O(e,t){let n=await D();if(n===void 0)throw new h(`keys-unavailable`);if(e===void 0){if(n.length>1)throw new h(`ambiguous-key`);let e=n[0];if(e===void 0)throw new h(`unknown-key`);if(!b(e,t))throw new h(`key-mismatch`);return e}let r=n.filter(t=>t.kid===e);if(r.length===0&&(await E(),n=l.keys??[],r=n.filter(t=>t.kid===e)),r.length===0)throw new h(`unknown-key`);let i=r.find(e=>b(e,t));if(i===void 0)throw new h(`key-mismatch`);return i}async function k(n){if(typeof n!=`string`||n.length===0)throw new h(`malformed`);if(n.length>8192)throw new h(`oversize`);let r;try{r=(0,t.decodeProtectedHeader)(n)}catch{throw new h(`malformed`)}if(typeof r.typ!=`string`||S(r.typ)!==f)throw new h(`wrong-type`);let i=e.algorithms.find(e=>e===r.alg);if(i===void 0)throw new h(`unsupported-algorithm`);if(r.kid!==void 0&&typeof r.kid!=`string`)throw new h(`malformed`);let o=await O(r.kid,i),s;try{s=await(0,t.importJWK)(o,i)}catch{throw new h(`key-mismatch`)}let c;try{({payload:c}=await(0,t.jwtVerify)(n,s,{issuer:e.issuer,audience:e.resource,algorithms:[i],typ:f,clockTolerance:60,currentDate:new Date(a()),requiredClaims:[`exp`,`sub`,`client_id`]}))}catch(e){throw new h(x(e))}let l=typeof c.scope==`string`?c.scope.split(` `):[];if(!e.requiredScopes.every(e=>l.includes(e)))throw new h(`missing-scope`);let d=c.sub,m=c.client_id;if(!(typeof d==`string`&&u.has(d)||typeof m==`string`&&p.has(m)))throw new h(`principal-not-allowed`);return{admitted:!0}}return{async verify(e){try{return await k(e)}catch(e){return{admitted:!1,refusal:e instanceof h?e.refusal:`malformed`}}}}}function _(e){if(!URL.canParse(e.issuer))throw Error(`access-token verifier: issuer is not a URL`);let t=new URL(e.issuer);if(t.protocol!==`https:`||t.search!==``||t.hash!==``)throw Error(`access-token verifier: issuer must be an https URL with no query or fragment`);if(!URL.canParse(e.resource))throw Error(`access-token verifier: resource is not a URL`);if(e.algorithms.length===0||!e.algorithms.every(e=>p.includes(e)))throw Error(`access-token verifier: algorithms must be a non-empty subset of RS256, ES256, EdDSA`);if(e.requiredScopes.length===0)throw Error(`access-token verifier: at least one required scope is needed`);if((e.allowedSubjects?.length??0)+(e.allowedClients?.length??0)===0)throw Error(`access-token verifier: name at least one allowed subject or client — this host is single-tenant, and issuer + audience + scope alone admit anyone the issuer serves`);return t}function v(e){let t=e.pathname===`/`?``:e.pathname.replace(/\/$/,``);return[`${e.origin}/.well-known/oauth-authorization-server${t}`,`${e.origin}${t}/.well-known/openid-configuration`]}function y(e){if(!(!C(e)||!Array.isArray(e.keys)||e.keys.length>32))return e.keys.filter(e=>C(e)&&typeof e.kty==`string`)}function b(e,t){let n=m[t];return e.kty!==n.kty||n.crv!==void 0&&e.crv!==n.crv||e.alg!==void 0&&e.alg!==t||e.use!==void 0&&e.use!==`sig`||e.key_ops!==void 0&&!e.key_ops.includes(`verify`)?!1:e.d===void 0}function x(e){let t=C(e)?e.code:void 0,n=C(e)?e.claim:void 0;switch(t){case`ERR_JWT_EXPIRED`:return`expired`;case`ERR_JWS_SIGNATURE_VERIFICATION_FAILED`:return`bad-signature`;case`ERR_JOSE_ALG_NOT_ALLOWED`:return`unsupported-algorithm`;case`ERR_JWT_CLAIM_VALIDATION_FAILED`:return n===`iss`?`wrong-issuer`:n===`aud`?`wrong-audience`:n===`nbf`?`not-yet-valid`:n===`typ`?`wrong-type`:`malformed`;default:return`malformed`}}function S(e){let t=e.toLowerCase();return t.startsWith(`application/`)?t.slice(12):t}function C(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}const w=6e4,T=4096,E=/^[\x21\x23-\x5b\x5d-\x7e]+$/,D=new r.BlockList;D.addSubnet(`127.0.0.0`,8,`ipv4`),D.addAddress(`::1`,`ipv6`);const O=new r.BlockList;O.addSubnet(`10.0.0.0`,8,`ipv4`),O.addSubnet(`172.16.0.0`,12,`ipv4`),O.addSubnet(`192.168.0.0`,16,`ipv4`),O.addSubnet(`169.254.0.0`,16,`ipv4`),O.addSubnet(`100.64.0.0`,10,`ipv4`),O.addSubnet(`fc00::`,7,`ipv6`),O.addSubnet(`fe80::`,10,`ipv6`);function k(e){if(e===void 0)return;let t=e.trim().toLowerCase(),n=t.startsWith(`::ffff:`)?t.slice(7):void 0;return n!==void 0&&(0,r.isIP)(n)===4?n:(0,r.isIP)(t)===0?void 0:t}function A(e){if(e===void 0)return`unknown`;let t=(0,r.isIP)(e)===4?`ipv4`:`ipv6`;return D.check(e,t)?`loopback`:O.check(e,t)?`private`:`public`}function j(e,t){let n;try{n=new URL(e)}catch{throw Error(`${t} public URL must be an absolute https URL`)}if(n.protocol!==`https:`)throw Error(`${t} public URL must use https`);if(n.username!==``||n.password!==``||n.search!==``||n.hash!==``)throw Error(`${t} public URL must not carry credentials, a query or a fragment`);return n}function M(e){let t=new Map;return n=>{let r=e(),i=t.get(n);if(i===void 0||r-i.startedAt>=w){if(t.delete(n),t.size>=T){for(let[e,n]of t)r-n.startedAt>=w&&t.delete(e);let e=t.keys().next();t.size>=T&&e.done!==!0&&t.delete(e.value)}i={count:0,startedAt:r},t.set(n,i)}i.count+=1;let a=w-(r-i.startedAt);return{throttled:i.count>20,retryAfterSeconds:Math.max(1,Math.ceil(a/1e3))}}}function N(e){let t=j(e.publicUrl,e.label),n=new Set;for(let t of e.trustedProxies??[]){let r=k(t);if(r===void 0)throw Error(`${e.label} trusted proxy must be a literal IP address`);n.add(r)}let r=new Set([t.host]);t.port===``&&r.add(`${t.hostname}:443`);let i=M(e.now??(()=>performance.now()));function a(e){let t=k(e.socket.remoteAddress);if(t===void 0||!n.has(t))return t;let r=e.headers[`x-forwarded-for`],i=(Array.isArray(r)?r.join(`,`):r??``).split(`,`);for(let e=i.length-1;e>=0;--e){let r=k(i[e]);if(r===void 0)return t;if(!n.has(r))return r}return t}return{url:t,checkNames(e,n){let i=e.headers.host?.toLowerCase(),a=e.headers.origin;return i===void 0||!r.has(i)||a!==void 0&&a!==t.origin?(n.writeHead(403).end(),!1):!0},fail(e){let t=a(e);return{remote:A(t),...i(t??`unknown`)}},remote(e){return A(a(e))}}}function P(e){let t=j(e.resource,e.label),n;try{n=new URL(e.issuer)}catch{throw Error(`${e.label} authorization issuer must be an absolute https URL`)}if(n.protocol!==`https:`)throw Error(`${e.label} authorization issuer must use https`);if(e.scopes.length===0||!e.scopes.every(e=>E.test(e)))throw Error(`${e.label} authorization needs at least one valid scope`);let r=`/.well-known/oauth-protected-resource${t.pathname===`/`?``:t.pathname}`,i=`${t.origin}${r}`,a=e.scopes.join(` `);return{wellKnownPath:r,metadata:JSON.stringify({resource:e.resource,authorization_servers:[e.issuer],scopes_supported:[...e.scopes],bearer_methods_supported:[`header`]}),challenges:{missing:`Bearer resource_metadata="${i}"`,invalid:`Bearer error="invalid_token", resource_metadata="${i}"`,scope:`Bearer error="insufficient_scope", scope="${a}", resource_metadata="${i}"`}}}function F(e,t,n){if(e.method!==`GET`){t.writeHead(405,{Allow:`GET`}).end();return}t.writeHead(200,{"Content-Type":`application/json`}).end(n.metadata)}function I(e,t,n,r){if(r.throttled)e.writeHead(429,{"Retry-After":String(r.retryAfterSeconds)}).end();else if(n===`missing-scope`)e.writeHead(403,{"WWW-Authenticate":t.challenges.scope}).end();else{let r=n===`missing-token`?t.challenges.missing:t.challenges.invalid;e.writeHead(401,{"WWW-Authenticate":r}).end()}}function L(e,t,n,r,i,a){if(i===`keys-unavailable`){a?.({refusal:i,remote:n.remote(e),throttled:!1}),t.writeHead(503).end();return}let o=n.fail(e);a?.({refusal:i,remote:o.remote,throttled:o.throttled}),I(t,r,i===`missing-token`||i===`missing-scope`?i:`invalid-token`,o)}const R=/^[0-9a-f]{64}$/,z=/^[A-Za-z0-9+/]*={0,2}$/,B=3e4,V=new Set([`declined`,`too-large`,`bad-name`,`exists`,`unsafe-path`,`integrity`,`protocol`,`closed`,`timeout`,`unavailable`]),H=[[0,31],[127,159],[8203,8207],[8232,8238],[8294,8297],[65279,65279]];function U(e){let t=``;for(let n of e){let e=n.codePointAt(0)??0;t+=H.some(([t,n])=>e>=t&&e<=n)?` `:n}return t}function W(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=0}function G(e){if(e.length>22360)return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!=`object`||!t)return;let n=t;switch(n.t){case`file-offer`:{let{transferId:e,name:t,size:r,sha256:i}=n;return typeof e!=`string`||e.length===0||e.length>128||typeof t!=`string`||t.length>1024||!W(r)||typeof i!=`string`||!R.test(i)?void 0:{t:`file-offer`,transferId:e,name:t,size:r,sha256:i}}case`file-accept`:return{t:`file-accept`};case`file-refuse`:{let e=n.reason,t=n.detail;return typeof e!=`string`||!V.has(e)?void 0:{t:`file-refuse`,reason:e,...typeof t==`string`?{detail:U(t).slice(0,500)}:{}}}case`file-chunk`:{let{seq:e,data:t}=n;return!W(e)||typeof t!=`string`||t===``||!z.test(t)?void 0:{t:`file-chunk`,seq:e,data:t}}case`file-credit`:return W(n.upTo)?{t:`file-credit`,upTo:n.upTo}:void 0;case`file-end`:return W(n.chunks)?{t:`file-end`,chunks:n.chunks}:void 0;case`file-done`:return{t:`file-done`};default:return}}var K=class extends Error{reason;detail;constructor(e,t){super(t??e),this.reason=e,this.detail=t}},q=class{capacity;queue=[];waiter;failure;stops=[];constructor(e,t){this.capacity=t,this.stops.push(e.onFrame(e=>{let t=G(e);if(t===void 0){this.fail(new K(`protocol`,`a frame could not be read`));return}if(this.waiter!==void 0){let{resolve:e}=this.waiter;this.waiter=void 0,e(t);return}if(this.queue.length>=this.capacity){this.fail(new K(`protocol`,`the peer sent past its credit`));return}this.queue.push(t)}),e.onClose(()=>this.fail(new K(`closed`,`the channel closed`))))}fail(e){if(this.failure!==void 0)return;this.failure=e,e.reason===`protocol`&&(this.queue.length=0);let t=this.waiter;this.waiter=void 0,t?.reject(e)}next(e){let t=this.queue.shift();return t===void 0?this.failure===void 0?new Promise((t,n)=>{let r=setTimeout(()=>{this.waiter=void 0,n(new K(`timeout`,`nothing arrived within ${e}ms`))},e);r.unref?.(),this.waiter={resolve:e=>{clearTimeout(r),t(e)},reject:e=>{clearTimeout(r),n(e)}}}):Promise.reject(this.failure):Promise.resolve(t)}get ended(){return this.failure!==void 0}stop(){for(let e of this.stops.splice(0))e()}};function J(e,t){try{e.send(JSON.stringify(t))}catch(e){throw new K(`closed`,e instanceof Error?e.message:String(e))}}function Y(e){return e instanceof K?e:new K(`unavailable`,e instanceof Error?e.message:String(e))}async function*X(e,t){let n=Buffer.alloc(0);for await(let r of e)for(n=n.length===0?Buffer.from(r):Buffer.concat([n,r]);n.length>=t;)yield n.subarray(0,t),n=n.subarray(t);n.length>0&&(yield n)}async function Z(e){let{channel:t,offer:n,source:r}=e,i=e.chunkBytes??16384,a=e.window??16,o=e.idleMs??B,s=new q(t,a+2);try{if(r.size!==n.size)throw new K(`integrity`,`the source is not the size offered`);J(t,{t:`file-offer`,...n});let c=await s.next(e.decisionMs??3e5);if(c.t===`file-refuse`)throw new K(c.reason,c.detail);if(c.t!==`file-accept`)throw new K(`protocol`,`expected an answer`);let l=0,u=0,d=0,f=async()=>{let e=await s.next(o);if(e.t===`file-refuse`)throw new K(e.reason,e.detail);if(e.t!==`file-credit`||e.upTo<=u||e.upTo>l)throw new K(`protocol`,`expected credit`);u=e.upTo};for await(let e of X(r.read(),i)){if(d+=e.length,d>n.size)throw new K(`integrity`,`the source grew while sending`);for(;l-u>=a;)await f();J(t,{t:`file-chunk`,seq:l,data:e.toString(`base64`)}),l+=1}if(d!==n.size)throw new K(`integrity`,`the source shrank while sending`);for(J(t,{t:`file-end`,chunks:l});;){let e=await s.next(o);if(e.t===`file-done`)return{ok:!0};if(e.t===`file-refuse`)throw new K(e.reason,e.detail);if(e.t!==`file-credit`||e.upTo<=u||e.upTo>l)throw new K(`protocol`,`expected credit or completion`);u=e.upTo}}catch(e){let t=Y(e);return{ok:!1,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail}}}finally{s.stop(),t.close()}}async function Q(t){let{channel:n}=t,r=t.maxBytes??33554432,i=t.window??16,a=t.idleMs??B,o=new q(n,i+1),s=new AbortController,c=n.onClose(()=>s.abort()),l,u,d=!0;try{let i=await o.next(a);if(i.t!==`file-offer`)throw new K(`protocol`,`expected an offer`);if(l={transferId:i.transferId,name:i.name,size:i.size,sha256:i.sha256},l.size>r)throw new K(`too-large`,`${l.size} bytes is over the ${r}-byte limit`);let c=await t.admit(l,s.signal);if(`refused`in c)throw new K(c.refused,c.detail);if(u=c.sink,o.ended)throw new K(`closed`,`the sender left before the answer`);J(n,{t:`file-accept`});let d=(0,e.createHash)(`sha256`),f=0,p=0;for(;;){let e=await o.next(a);if(e.t===`file-end`){if(e.chunks!==f||p!==l.size)throw new K(`integrity`,`the content is not the size offered`);if(d.digest(`hex`)!==l.sha256)throw new K(`integrity`,`the content does not match the offered sha256`);let t=await u.commit();return u=void 0,J(n,{t:`file-done`}),{ok:!0,offer:l,location:t}}if(e.t!==`file-chunk`||e.seq!==f)throw new K(`protocol`,`expected the next chunk`);let t=Buffer.from(e.data,`base64`);if(p+=t.length,p>l.size)throw new K(`integrity`,`more content than offered`);d.update(t),await u.write(t),f+=1,J(n,{t:`file-credit`,upTo:f})}}catch(e){let t=Y(e);if(d=t.reason!==`protocol`,u!==void 0&&await u.discard().catch(()=>void 0),t.reason!==`closed`&&t.reason!==`timeout`)try{J(n,{t:`file-refuse`,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail}})}catch{}return{ok:!1,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail},...l===void 0?{}:{offer:l}}}finally{d&&await $(s.signal,5e3),c(),o.stop(),n.close()}}function $(e,t){return e.aborted?Promise.resolve():new Promise(n=>{let r=setTimeout(i,t);r.unref?.(),e.addEventListener(`abort`,i,{once:!0});function i(){clearTimeout(r),e.removeEventListener(`abort`,i),n()}})}function ee(e,t,n){let r=new i.PassThrough,a=!1;return e.once(`end`,n.onInputClose),e.once(`close`,n.onInputClose),t.once(`close`,n.onOutputClose),t.on(`error`,n.onOutputError),e.pipe(r),{bridge:r,release(){a||(a=!0,e.off(`end`,n.onInputClose),e.off(`close`,n.onInputClose),t.off(`close`,n.onOutputClose),t.off(`error`,n.onOutputError),e.unpipe(r),r.destroy())}}}exports.DEFAULT_MAX_FILE_BYTES=33554432,exports.FILE_CHUNK_BYTES=16384,exports.FILE_CREDIT_WINDOW=16,exports.attachStdioCarrier=ee,exports.bearerCredential=c,exports.createAccessTokenVerifier=g,exports.createBearerResourceServer=N,exports.credentialMatches=s,exports.describeProtectedResource=P,exports.mintTransportToken=a,exports.parsePublicHttpsUrl=j,exports.receiveFileOverChannel=Q,exports.refuseAccessToken=L,exports.refuseBearerToken=I,exports.resolveAdmission=o,exports.sealHandoffRecord=l,exports.sendFileOverChannel=Z,exports.serveProtectedResourceMetadata=F,exports.verifyHandoffPayload=u;
@@ -3,6 +3,7 @@ import { IAccessTokenVerifier, IAccessTokenVerifierConfig, ITransportAdmission,
3
3
  import { IFileFrameChannel, IFileOffer, IHandoffIntegrity } from "@robota-sdk/agent-interface-session-mobility";
4
4
  import { IEgressDeps } from "@robota-sdk/agent-core/node";
5
5
  import { IncomingMessage, ServerResponse } from "node:http";
6
+ import { PassThrough, Readable, Writable } from "node:stream";
6
7
  //#region src/node/admission.d.ts
7
8
  /**
8
9
  * Mint a per-launch credential.
@@ -295,5 +296,22 @@ interface IReceiveFileOptions {
295
296
  */
296
297
  declare function receiveFileOverChannel(options: IReceiveFileOptions): Promise<TFileReceiveOutcome>;
297
298
  //#endregion
298
- export { DEFAULT_MAX_FILE_BYTES, FILE_CHUNK_BYTES, FILE_CREDIT_WINDOW, type IAccessTokenVerifierDeps, type IBearerFailure, type IBearerRefusalRecord, type IBearerResourceServer, type IFileSink, type IFileSource, type IIntegrityVerdict, type IProtectedResource, type IReceiveFileOptions, type ISendFileOptions, type TBearerRefusal, type TFileAdmission, type TFileReceiveOutcome, type TFileSendOutcome, type TFileTransferRefusal, type TIntegrityFailure, type TRemoteAddressClass, bearerCredential, createAccessTokenVerifier, createBearerResourceServer, credentialMatches, describeProtectedResource, mintTransportToken, parsePublicHttpsUrl, receiveFileOverChannel, refuseAccessToken, refuseBearerToken, resolveAdmission, sealHandoffRecord, sendFileOverChannel, serveProtectedResourceMetadata, verifyHandoffPayload };
299
+ //#region src/node/stdio-carrier.d.ts
300
+ interface IStdioCarrierHandlers {
301
+ /** The peer ended its side: `end` or `close` on the input. May fire more than once. */
302
+ readonly onInputClose: () => void;
303
+ /** The output closed. */
304
+ readonly onOutputClose: () => void;
305
+ /** The output failed to accept a write. */
306
+ readonly onOutputError: (error: Error) => void;
307
+ }
308
+ interface IStdioCarrier {
309
+ /** Hand this to the protocol SDK in place of the input; destroying it never destroys the input. */
310
+ readonly bridge: PassThrough;
311
+ /** Remove the listeners, unpipe and destroy the bridge. Safe to call again; the streams stay open. */
312
+ release(): void;
313
+ }
314
+ declare function attachStdioCarrier(input: Readable, output: Writable, handlers: IStdioCarrierHandlers): IStdioCarrier;
315
+ //#endregion
316
+ export { DEFAULT_MAX_FILE_BYTES, FILE_CHUNK_BYTES, FILE_CREDIT_WINDOW, type IAccessTokenVerifierDeps, type IBearerFailure, type IBearerRefusalRecord, type IBearerResourceServer, type IFileSink, type IFileSource, type IIntegrityVerdict, type IProtectedResource, type IReceiveFileOptions, type ISendFileOptions, type IStdioCarrier, type IStdioCarrierHandlers, type TBearerRefusal, type TFileAdmission, type TFileReceiveOutcome, type TFileSendOutcome, type TFileTransferRefusal, type TIntegrityFailure, type TRemoteAddressClass, attachStdioCarrier, bearerCredential, createAccessTokenVerifier, createBearerResourceServer, credentialMatches, describeProtectedResource, mintTransportToken, parsePublicHttpsUrl, receiveFileOverChannel, refuseAccessToken, refuseBearerToken, resolveAdmission, sealHandoffRecord, sendFileOverChannel, serveProtectedResourceMetadata, verifyHandoffPayload };
299
317
  //# sourceMappingURL=node.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.cts","names":[],"sources":["../../src/node/admission.ts","../../src/node/handoff-manifest.ts","../../src/node/access-token-verifier.ts","../../src/node/bearer-resource-gate.ts","../../src/node/file-transfer.ts"],"mappings":";;;;;;;;;;;;iBA4BgB;;;;;;;;;;;;;;;;;;;;;;;;iBA+BA,iBACd,SAAQ,4BAA4B,sBACnC;;;;;;;;;;;;;;;;;;;iBAoDa,kBAAkB,kBAAkB;iBA8BpC,iBAAiB;;;;;;;;;;;iBCpHjB,kBAAkB,QAAQ;EACxC;EACA,WAAW;;;KAcD;UAEK;WACN;WACA,UAAU;;WAEV;WACA;;;;;;;;;iBAUK,qBACd,oBACA,WAAW,oBACV;;;;UCCc;WACN,QAAQ;WACR,SAAS;;WAET;;WAEA;;;;;;;;;iBAwBK,0BACd,QAAQ,4BACR,OAAM,2BACL;;;;KC9DS;;UAGK;WACN,QAAQ;WACR;WACA;;;UAIM;;WAEN,KAAK;;;;;EAKd,WAAW,KAAK,iBAAiB,KAAK;;EAEtC,KAAK,KAAK,kBAAkB;;EAE5B,OAAO,KAAK,kBAAkB;;;UAIf;;WAEN;WACA;WACA;aACE;aACA;aACA;;;;iBAkCG,oBAAoB,mBAAmB,gBAAgB;;;;;iBAoDvD,2BAA2B;WAChC;;WAEA;WACA;WACA;IACP;;;;;iBA0DY,0BAA0B;;WAE/B;WACA;WACA;WACA;IACP;;iBAoCY,+BACd,KAAK,iBACL,KAAK,gBACL,UAAU;;;;;;iBAcI,kBACd,KAAK,gBACL,UAAU,oBACV,8DACA,SAAS;;KAcC,iBAAiB;;UAGZ;WACN,SAAS;WACT,QAAQ;;WAER;;;;;;;iBAQK,kBACd,KAAK,iBACL,KAAK,gBACL,QAAQ,uBACR,UAAU,oBACV,SAAS,gBACT,SAAS,QAAQ;;;;KC7RP;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4BC;;;;;cAMA;;cAEA;;UAcI;WACN;EACT,QAAQ,cAAc;;;UAIP;EACf,MAAM,OAAO,aAAa;;EAE1B,UAAU;;EAEV,WAAW;;;KAID;WACG,MAAM;;WACN,SAAS;WAA+B;;KAE3C;WACG;;WACA;WAAoB,QAAQ;WAA+B;;KAE9D;WACG;WAAmB,OAAO;WAAqB;;WAE/C;WACA,QAAQ;WACR;WACA,QAAQ;;UA4NN;WACN,SAAS;WACT,OAAO;WACP,QAAQ;WACR;WACA;WACA;;WAEA;;;;;;iBAOW,oBAAoB,SAAS,mBAAmB,QAAQ;UA2D7D;WACN,SAAS;;WAET;;;;;WAKA,QAAQ,OAAO,YAAY,QAAQ,gBAAgB,QAAQ;WAC3D;WACA;;;;;;iBAOW,uBACpB,SAAS,sBACR,QAAQ"}
1
+ {"version":3,"file":"node.d.cts","names":[],"sources":["../../src/node/admission.ts","../../src/node/handoff-manifest.ts","../../src/node/access-token-verifier.ts","../../src/node/bearer-resource-gate.ts","../../src/node/file-transfer.ts","../../src/node/stdio-carrier.ts"],"mappings":";;;;;;;;;;;;;iBA4BgB;;;;;;;;;;;;;;;;;;;;;;;;iBA+BA,iBACd,SAAQ,4BAA4B,sBACnC;;;;;;;;;;;;;;;;;;;iBAoDa,kBAAkB,kBAAkB;iBA8BpC,iBAAiB;;;;;;;;;;;iBCpHjB,kBAAkB,QAAQ;EACxC;EACA,WAAW;;;KAcD;UAEK;WACN;WACA,UAAU;;WAEV;WACA;;;;;;;;;iBAUK,qBACd,oBACA,WAAW,oBACV;;;;UCCc;WACN,QAAQ;WACR,SAAS;;WAET;;WAEA;;;;;;;;;iBAwBK,0BACd,QAAQ,4BACR,OAAM,2BACL;;;;KC9DS;;UAGK;WACN,QAAQ;WACR;WACA;;;UAIM;;WAEN,KAAK;;;;;EAKd,WAAW,KAAK,iBAAiB,KAAK;;EAEtC,KAAK,KAAK,kBAAkB;;EAE5B,OAAO,KAAK,kBAAkB;;;UAIf;;WAEN;WACA;WACA;aACE;aACA;aACA;;;;iBAkCG,oBAAoB,mBAAmB,gBAAgB;;;;;iBAoDvD,2BAA2B;WAChC;;WAEA;WACA;WACA;IACP;;;;;iBA0DY,0BAA0B;;WAE/B;WACA;WACA;WACA;IACP;;iBAoCY,+BACd,KAAK,iBACL,KAAK,gBACL,UAAU;;;;;;iBAcI,kBACd,KAAK,gBACL,UAAU,oBACV,8DACA,SAAS;;KAcC,iBAAiB;;UAGZ;WACN,SAAS;WACT,QAAQ;;WAER;;;;;;;iBAQK,kBACd,KAAK,iBACL,KAAK,gBACL,QAAQ,uBACR,UAAU,oBACV,SAAS,gBACT,SAAS,QAAQ;;;;KC7RP;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4BC;;;;;cAMA;;cAEA;;UAcI;WACN;EACT,QAAQ,cAAc;;;UAIP;EACf,MAAM,OAAO,aAAa;;EAE1B,UAAU;;EAEV,WAAW;;;KAID;WACG,MAAM;;WACN,SAAS;WAA+B;;KAE3C;WACG;;WACA;WAAoB,QAAQ;WAA+B;;KAE9D;WACG;WAAmB,OAAO;WAAqB;;WAE/C;WACA,QAAQ;WACR;WACA,QAAQ;;UA4NN;WACN,SAAS;WACT,OAAO;WACP,QAAQ;WACR;WACA;WACA;;WAEA;;;;;;iBAOW,oBAAoB,SAAS,mBAAmB,QAAQ;UA2D7D;WACN,SAAS;;WAET;;;;;WAKA,QAAQ,OAAO,YAAY,QAAQ,gBAAgB,QAAQ;WAC3D;WACA;;;;;;iBAOW,uBACpB,SAAS,sBACR,QAAQ;;;UCrZM;;WAEN;;WAEA;;WAEA,gBAAgB,OAAO;;UAGjB;;WAEN,QAAQ;;EAEjB;;iBAGc,mBACd,OAAO,UACP,QAAQ,UACR,UAAU,wBACT"}
@@ -1,5 +1,6 @@
1
1
  import { IInteractiveSessionRecord } from "@robota-sdk/agent-interface-session";
2
2
  import { IEgressDeps } from "@robota-sdk/agent-core/node";
3
+ import { PassThrough, Readable, Writable } from "node:stream";
3
4
  import { IAccessTokenVerifier, IAccessTokenVerifierConfig, ITransportAdmission, ITransportAdmissionConfig, TAccessTokenRefusal } from "@robota-sdk/agent-interface-transport";
4
5
  import { IFileFrameChannel, IFileOffer, IHandoffIntegrity } from "@robota-sdk/agent-interface-session-mobility";
5
6
  import { IncomingMessage, ServerResponse } from "node:http";
@@ -295,5 +296,22 @@ interface IReceiveFileOptions {
295
296
  */
296
297
  declare function receiveFileOverChannel(options: IReceiveFileOptions): Promise<TFileReceiveOutcome>;
297
298
  //#endregion
298
- export { DEFAULT_MAX_FILE_BYTES, FILE_CHUNK_BYTES, FILE_CREDIT_WINDOW, type IAccessTokenVerifierDeps, type IBearerFailure, type IBearerRefusalRecord, type IBearerResourceServer, type IFileSink, type IFileSource, type IIntegrityVerdict, type IProtectedResource, type IReceiveFileOptions, type ISendFileOptions, type TBearerRefusal, type TFileAdmission, type TFileReceiveOutcome, type TFileSendOutcome, type TFileTransferRefusal, type TIntegrityFailure, type TRemoteAddressClass, bearerCredential, createAccessTokenVerifier, createBearerResourceServer, credentialMatches, describeProtectedResource, mintTransportToken, parsePublicHttpsUrl, receiveFileOverChannel, refuseAccessToken, refuseBearerToken, resolveAdmission, sealHandoffRecord, sendFileOverChannel, serveProtectedResourceMetadata, verifyHandoffPayload };
299
+ //#region src/node/stdio-carrier.d.ts
300
+ interface IStdioCarrierHandlers {
301
+ /** The peer ended its side: `end` or `close` on the input. May fire more than once. */
302
+ readonly onInputClose: () => void;
303
+ /** The output closed. */
304
+ readonly onOutputClose: () => void;
305
+ /** The output failed to accept a write. */
306
+ readonly onOutputError: (error: Error) => void;
307
+ }
308
+ interface IStdioCarrier {
309
+ /** Hand this to the protocol SDK in place of the input; destroying it never destroys the input. */
310
+ readonly bridge: PassThrough;
311
+ /** Remove the listeners, unpipe and destroy the bridge. Safe to call again; the streams stay open. */
312
+ release(): void;
313
+ }
314
+ declare function attachStdioCarrier(input: Readable, output: Writable, handlers: IStdioCarrierHandlers): IStdioCarrier;
315
+ //#endregion
316
+ export { DEFAULT_MAX_FILE_BYTES, FILE_CHUNK_BYTES, FILE_CREDIT_WINDOW, type IAccessTokenVerifierDeps, type IBearerFailure, type IBearerRefusalRecord, type IBearerResourceServer, type IFileSink, type IFileSource, type IIntegrityVerdict, type IProtectedResource, type IReceiveFileOptions, type ISendFileOptions, type IStdioCarrier, type IStdioCarrierHandlers, type TBearerRefusal, type TFileAdmission, type TFileReceiveOutcome, type TFileSendOutcome, type TFileTransferRefusal, type TIntegrityFailure, type TRemoteAddressClass, attachStdioCarrier, bearerCredential, createAccessTokenVerifier, createBearerResourceServer, credentialMatches, describeProtectedResource, mintTransportToken, parsePublicHttpsUrl, receiveFileOverChannel, refuseAccessToken, refuseBearerToken, resolveAdmission, sealHandoffRecord, sendFileOverChannel, serveProtectedResourceMetadata, verifyHandoffPayload };
299
317
  //# sourceMappingURL=node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","names":[],"sources":["../../src/node/admission.ts","../../src/node/handoff-manifest.ts","../../src/node/access-token-verifier.ts","../../src/node/bearer-resource-gate.ts","../../src/node/file-transfer.ts"],"mappings":";;;;;;;;;;;;iBA4BgB;;;;;;;;;;;;;;;;;;;;;;;;iBA+BA,iBACd,SAAQ,4BAA4B,sBACnC;;;;;;;;;;;;;;;;;;;iBAoDa,kBAAkB,kBAAkB;iBA8BpC,iBAAiB;;;;;;;;;;;iBCpHjB,kBAAkB,QAAQ;EACxC;EACA,WAAW;;;KAcD;UAEK;WACN;WACA,UAAU;;WAEV;WACA;;;;;;;;;iBAUK,qBACd,oBACA,WAAW,oBACV;;;;UCCc;WACN,QAAQ;WACR,SAAS;;WAET;;WAEA;;;;;;;;;iBAwBK,0BACd,QAAQ,4BACR,OAAM,2BACL;;;;KC9DS;;UAGK;WACN,QAAQ;WACR;WACA;;;UAIM;;WAEN,KAAK;;;;;EAKd,WAAW,KAAK,iBAAiB,KAAK;;EAEtC,KAAK,KAAK,kBAAkB;;EAE5B,OAAO,KAAK,kBAAkB;;;UAIf;;WAEN;WACA;WACA;aACE;aACA;aACA;;;;iBAkCG,oBAAoB,mBAAmB,gBAAgB;;;;;iBAoDvD,2BAA2B;WAChC;;WAEA;WACA;WACA;IACP;;;;;iBA0DY,0BAA0B;;WAE/B;WACA;WACA;WACA;IACP;;iBAoCY,+BACd,KAAK,iBACL,KAAK,gBACL,UAAU;;;;;;iBAcI,kBACd,KAAK,gBACL,UAAU,oBACV,8DACA,SAAS;;KAcC,iBAAiB;;UAGZ;WACN,SAAS;WACT,QAAQ;;WAER;;;;;;;iBAQK,kBACd,KAAK,iBACL,KAAK,gBACL,QAAQ,uBACR,UAAU,oBACV,SAAS,gBACT,SAAS,QAAQ;;;;KC7RP;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4BC;;;;;cAMA;;cAEA;;UAcI;WACN;EACT,QAAQ,cAAc;;;UAIP;EACf,MAAM,OAAO,aAAa;;EAE1B,UAAU;;EAEV,WAAW;;;KAID;WACG,MAAM;;WACN,SAAS;WAA+B;;KAE3C;WACG;;WACA;WAAoB,QAAQ;WAA+B;;KAE9D;WACG;WAAmB,OAAO;WAAqB;;WAE/C;WACA,QAAQ;WACR;WACA,QAAQ;;UA4NN;WACN,SAAS;WACT,OAAO;WACP,QAAQ;WACR;WACA;WACA;;WAEA;;;;;;iBAOW,oBAAoB,SAAS,mBAAmB,QAAQ;UA2D7D;WACN,SAAS;;WAET;;;;;WAKA,QAAQ,OAAO,YAAY,QAAQ,gBAAgB,QAAQ;WAC3D;WACA;;;;;;iBAOW,uBACpB,SAAS,sBACR,QAAQ"}
1
+ {"version":3,"file":"node.d.ts","names":[],"sources":["../../src/node/admission.ts","../../src/node/handoff-manifest.ts","../../src/node/access-token-verifier.ts","../../src/node/bearer-resource-gate.ts","../../src/node/file-transfer.ts","../../src/node/stdio-carrier.ts"],"mappings":";;;;;;;;;;;;;iBA4BgB;;;;;;;;;;;;;;;;;;;;;;;;iBA+BA,iBACd,SAAQ,4BAA4B,sBACnC;;;;;;;;;;;;;;;;;;;iBAoDa,kBAAkB,kBAAkB;iBA8BpC,iBAAiB;;;;;;;;;;;iBCpHjB,kBAAkB,QAAQ;EACxC;EACA,WAAW;;;KAcD;UAEK;WACN;WACA,UAAU;;WAEV;WACA;;;;;;;;;iBAUK,qBACd,oBACA,WAAW,oBACV;;;;UCCc;WACN,QAAQ;WACR,SAAS;;WAET;;WAEA;;;;;;;;;iBAwBK,0BACd,QAAQ,4BACR,OAAM,2BACL;;;;KC9DS;;UAGK;WACN,QAAQ;WACR;WACA;;;UAIM;;WAEN,KAAK;;;;;EAKd,WAAW,KAAK,iBAAiB,KAAK;;EAEtC,KAAK,KAAK,kBAAkB;;EAE5B,OAAO,KAAK,kBAAkB;;;UAIf;;WAEN;WACA;WACA;aACE;aACA;aACA;;;;iBAkCG,oBAAoB,mBAAmB,gBAAgB;;;;;iBAoDvD,2BAA2B;WAChC;;WAEA;WACA;WACA;IACP;;;;;iBA0DY,0BAA0B;;WAE/B;WACA;WACA;WACA;IACP;;iBAoCY,+BACd,KAAK,iBACL,KAAK,gBACL,UAAU;;;;;;iBAcI,kBACd,KAAK,gBACL,UAAU,oBACV,8DACA,SAAS;;KAcC,iBAAiB;;UAGZ;WACN,SAAS;WACT,QAAQ;;WAER;;;;;;;iBAQK,kBACd,KAAK,iBACL,KAAK,gBACL,QAAQ,uBACR,UAAU,oBACV,SAAS,gBACT,SAAS,QAAQ;;;;KC7RP;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4BC;;;;;cAMA;;cAEA;;UAcI;WACN;EACT,QAAQ,cAAc;;;UAIP;EACf,MAAM,OAAO,aAAa;;EAE1B,UAAU;;EAEV,WAAW;;;KAID;WACG,MAAM;;WACN,SAAS;WAA+B;;KAE3C;WACG;;WACA;WAAoB,QAAQ;WAA+B;;KAE9D;WACG;WAAmB,OAAO;WAAqB;;WAE/C;WACA,QAAQ;WACR;WACA,QAAQ;;UA4NN;WACN,SAAS;WACT,OAAO;WACP,QAAQ;WACR;WACA;WACA;;WAEA;;;;;;iBAOW,oBAAoB,SAAS,mBAAmB,QAAQ;UA2D7D;WACN,SAAS;;WAET;;;;;WAKA,QAAQ,OAAO,YAAY,QAAQ,gBAAgB,QAAQ;WAC3D;WACA;;;;;;iBAOW,uBACpB,SAAS,sBACR,QAAQ;;;UCrZM;;WAEN;;WAEA;;WAEA,gBAAgB,OAAO;;UAGjB;;WAEN,QAAQ;;EAEjB;;iBAGc,mBACd,OAAO,UACP,QAAQ,UACR,UAAU,wBACT"}
package/dist/node/node.js CHANGED
@@ -1,2 +1,2 @@
1
- import{createHash as e,randomBytes as t,timingSafeEqual as n}from"node:crypto";import{decodeProtectedHeader as r,importJWK as i,jwtVerify as a}from"jose";import{fetchWithEgressPolicy as o}from"@robota-sdk/agent-core/node";import{BlockList as s,isIP as c}from"node:net";function l(){let e=t(32).toString(`hex`);if(e.length!==64)throw Error(`transport admission: could not mint a credential of the required length`);return e}function u(e={}){let t=`open`in e&&e.open===!0,n=e.token!==void 0&&e.token!==null&&e.token!==``;if(n&&t)throw Error("transport admission: a token and `open: true` are contradictory — one requires a credential and the other requires none. Asking for both means one of the two beliefs behind the call is wrong; say which is intended.");if(n)return{token:e.token};if(e.token===null||t){let t=e.openReason?.trim();if(!t)throw Error("transport admission: an open transport requires `openReason`. An open transport is a decision, and a decision nobody wrote down is indistinguishable from an oversight.");return{token:null,openReason:t}}return{token:l()}}function d(e,t){if(t===void 0)return!1;let r=Buffer.from(e,`utf8`),i=Buffer.from(t,`utf8`);return r.length===i.length&&n(r,i)}function f(e){if(!e||e.slice(0,6).toLowerCase()!==`bearer`)return;let t=e.slice(6);if(t===``||t[0]!==` `&&t[0]!==` `)return;let n=t.replace(/^[ \t]+/,``);return n===``?void 0:n}function p(t){let n=JSON.stringify(t),r=Buffer.from(n,`utf8`);return{serialized:n,integrity:{digest:e(`sha256`).update(r).digest(`base64url`),byteLength:r.byteLength}}}function m(t,n){let r=Buffer.from(t,`utf8`);return r.byteLength===n.byteLength?e(`sha256`).update(r).digest(`base64url`)===n.digest?{intact:!0}:{intact:!1,failure:`digest-mismatch`}:{intact:!1,failure:`truncated`,expectedBytes:n.byteLength,actualBytes:r.byteLength}}const h=10*6e4,g=`at+jwt`,_=[`RS256`,`ES256`,`EdDSA`],v={RS256:{kty:`RSA`},ES256:{kty:`EC`,crv:`P-256`},EdDSA:{kty:`OKP`,crv:`Ed25519`}};var y=class{refusal;constructor(e){this.refusal=e}};function b(e,t={}){let n=ee(e),s=t.now??Date.now,c=t.monotonicNow??(()=>performance.now()),l={fetch:t.fetch,lookup:t.lookup},u=n.hostname.replace(/^\[|\]$/g,``).toLowerCase(),d={keys:void 0,jwksUri:void 0,fetchedAt:void 0,lastAttemptAt:void 0,inflight:void 0},f=new Set(e.allowedSubjects??[]),p=new Set(e.allowedClients??[]);async function m(e){if(new URL(e).protocol!==`https:`)return;let t=await o(e,{headers:{accept:`application/json`},timeoutMs:1e4,maxResponseBytes:65536},{allowedHosts:[u],maxRedirects:0},l);if(!(!t.ok||t.status!==200))return JSON.parse(new TextDecoder().decode(t.body))}async function _(){for(let t of x(n)){let n=await m(t).catch(()=>void 0);if(!E(n))continue;if(n.issuer!==e.issuer)return;let r=n.jwks_uri;return typeof r!=`string`||!URL.canParse(r)?void 0:new URL(r).protocol===`https:`?r:void 0}}function v(){return d.fetchedAt===void 0?1/0:c()-d.fetchedAt}async function b(){d.lastAttemptAt=c();try{if(v()>=h&&(d.jwksUri=void 0),d.jwksUri??=await _(),d.jwksUri===void 0)return;let e=S(await m(d.jwksUri));if(e===void 0)return;d.keys=e,d.fetchedAt=c()}catch{}}async function D(){if(d.inflight!==void 0)return d.inflight;if(!(d.lastAttemptAt!==void 0&&c()-d.lastAttemptAt<3e4))return d.inflight=b().finally(()=>{d.inflight=void 0}),d.inflight}async function O(){return v()>=h&&await D(),v()>=36e5&&(d.keys=void 0,d.jwksUri=void 0,d.fetchedAt=void 0),d.keys}async function k(e,t){let n=await O();if(n===void 0)throw new y(`keys-unavailable`);if(e===void 0){if(n.length>1)throw new y(`ambiguous-key`);let e=n[0];if(e===void 0)throw new y(`unknown-key`);if(!C(e,t))throw new y(`key-mismatch`);return e}let r=n.filter(t=>t.kid===e);if(r.length===0&&(await D(),n=d.keys??[],r=n.filter(t=>t.kid===e)),r.length===0)throw new y(`unknown-key`);let i=r.find(e=>C(e,t));if(i===void 0)throw new y(`key-mismatch`);return i}async function A(t){if(typeof t!=`string`||t.length===0)throw new y(`malformed`);if(t.length>8192)throw new y(`oversize`);let n;try{n=r(t)}catch{throw new y(`malformed`)}if(typeof n.typ!=`string`||T(n.typ)!==g)throw new y(`wrong-type`);let o=e.algorithms.find(e=>e===n.alg);if(o===void 0)throw new y(`unsupported-algorithm`);if(n.kid!==void 0&&typeof n.kid!=`string`)throw new y(`malformed`);let c=await k(n.kid,o),l;try{l=await i(c,o)}catch{throw new y(`key-mismatch`)}let u;try{({payload:u}=await a(t,l,{issuer:e.issuer,audience:e.resource,algorithms:[o],typ:g,clockTolerance:60,currentDate:new Date(s()),requiredClaims:[`exp`,`sub`,`client_id`]}))}catch(e){throw new y(w(e))}let d=typeof u.scope==`string`?u.scope.split(` `):[];if(!e.requiredScopes.every(e=>d.includes(e)))throw new y(`missing-scope`);let m=u.sub,h=u.client_id;if(!(typeof m==`string`&&f.has(m)||typeof h==`string`&&p.has(h)))throw new y(`principal-not-allowed`);return{admitted:!0}}return{async verify(e){try{return await A(e)}catch(e){return{admitted:!1,refusal:e instanceof y?e.refusal:`malformed`}}}}}function ee(e){if(!URL.canParse(e.issuer))throw Error(`access-token verifier: issuer is not a URL`);let t=new URL(e.issuer);if(t.protocol!==`https:`||t.search!==``||t.hash!==``)throw Error(`access-token verifier: issuer must be an https URL with no query or fragment`);if(!URL.canParse(e.resource))throw Error(`access-token verifier: resource is not a URL`);if(e.algorithms.length===0||!e.algorithms.every(e=>_.includes(e)))throw Error(`access-token verifier: algorithms must be a non-empty subset of RS256, ES256, EdDSA`);if(e.requiredScopes.length===0)throw Error(`access-token verifier: at least one required scope is needed`);if((e.allowedSubjects?.length??0)+(e.allowedClients?.length??0)===0)throw Error(`access-token verifier: name at least one allowed subject or client — this host is single-tenant, and issuer + audience + scope alone admit anyone the issuer serves`);return t}function x(e){let t=e.pathname===`/`?``:e.pathname.replace(/\/$/,``);return[`${e.origin}/.well-known/oauth-authorization-server${t}`,`${e.origin}${t}/.well-known/openid-configuration`]}function S(e){if(!(!E(e)||!Array.isArray(e.keys)||e.keys.length>32))return e.keys.filter(e=>E(e)&&typeof e.kty==`string`)}function C(e,t){let n=v[t];return e.kty!==n.kty||n.crv!==void 0&&e.crv!==n.crv||e.alg!==void 0&&e.alg!==t||e.use!==void 0&&e.use!==`sig`||e.key_ops!==void 0&&!e.key_ops.includes(`verify`)?!1:e.d===void 0}function w(e){let t=E(e)?e.code:void 0,n=E(e)?e.claim:void 0;switch(t){case`ERR_JWT_EXPIRED`:return`expired`;case`ERR_JWS_SIGNATURE_VERIFICATION_FAILED`:return`bad-signature`;case`ERR_JOSE_ALG_NOT_ALLOWED`:return`unsupported-algorithm`;case`ERR_JWT_CLAIM_VALIDATION_FAILED`:return n===`iss`?`wrong-issuer`:n===`aud`?`wrong-audience`:n===`nbf`?`not-yet-valid`:n===`typ`?`wrong-type`:`malformed`;default:return`malformed`}}function T(e){let t=e.toLowerCase();return t.startsWith(`application/`)?t.slice(12):t}function E(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}const D=6e4,O=4096,k=/^[\x21\x23-\x5b\x5d-\x7e]+$/,A=new s;A.addSubnet(`127.0.0.0`,8,`ipv4`),A.addAddress(`::1`,`ipv6`);const j=new s;j.addSubnet(`10.0.0.0`,8,`ipv4`),j.addSubnet(`172.16.0.0`,12,`ipv4`),j.addSubnet(`192.168.0.0`,16,`ipv4`),j.addSubnet(`169.254.0.0`,16,`ipv4`),j.addSubnet(`100.64.0.0`,10,`ipv4`),j.addSubnet(`fc00::`,7,`ipv6`),j.addSubnet(`fe80::`,10,`ipv6`);function M(e){if(e===void 0)return;let t=e.trim().toLowerCase(),n=t.startsWith(`::ffff:`)?t.slice(7):void 0;return n!==void 0&&c(n)===4?n:c(t)===0?void 0:t}function N(e){if(e===void 0)return`unknown`;let t=c(e)===4?`ipv4`:`ipv6`;return A.check(e,t)?`loopback`:j.check(e,t)?`private`:`public`}function P(e,t){let n;try{n=new URL(e)}catch{throw Error(`${t} public URL must be an absolute https URL`)}if(n.protocol!==`https:`)throw Error(`${t} public URL must use https`);if(n.username!==``||n.password!==``||n.search!==``||n.hash!==``)throw Error(`${t} public URL must not carry credentials, a query or a fragment`);return n}function F(e){let t=new Map;return n=>{let r=e(),i=t.get(n);if(i===void 0||r-i.startedAt>=D){if(t.delete(n),t.size>=O){for(let[e,n]of t)r-n.startedAt>=D&&t.delete(e);let e=t.keys().next();t.size>=O&&e.done!==!0&&t.delete(e.value)}i={count:0,startedAt:r},t.set(n,i)}i.count+=1;let a=D-(r-i.startedAt);return{throttled:i.count>20,retryAfterSeconds:Math.max(1,Math.ceil(a/1e3))}}}function I(e){let t=P(e.publicUrl,e.label),n=new Set;for(let t of e.trustedProxies??[]){let r=M(t);if(r===void 0)throw Error(`${e.label} trusted proxy must be a literal IP address`);n.add(r)}let r=new Set([t.host]);t.port===``&&r.add(`${t.hostname}:443`);let i=F(e.now??(()=>performance.now()));function a(e){let t=M(e.socket.remoteAddress);if(t===void 0||!n.has(t))return t;let r=e.headers[`x-forwarded-for`],i=(Array.isArray(r)?r.join(`,`):r??``).split(`,`);for(let e=i.length-1;e>=0;--e){let r=M(i[e]);if(r===void 0)return t;if(!n.has(r))return r}return t}return{url:t,checkNames(e,n){let i=e.headers.host?.toLowerCase(),a=e.headers.origin;return i===void 0||!r.has(i)||a!==void 0&&a!==t.origin?(n.writeHead(403).end(),!1):!0},fail(e){let t=a(e);return{remote:N(t),...i(t??`unknown`)}},remote(e){return N(a(e))}}}function L(e){let t=P(e.resource,e.label),n;try{n=new URL(e.issuer)}catch{throw Error(`${e.label} authorization issuer must be an absolute https URL`)}if(n.protocol!==`https:`)throw Error(`${e.label} authorization issuer must use https`);if(e.scopes.length===0||!e.scopes.every(e=>k.test(e)))throw Error(`${e.label} authorization needs at least one valid scope`);let r=`/.well-known/oauth-protected-resource${t.pathname===`/`?``:t.pathname}`,i=`${t.origin}${r}`,a=e.scopes.join(` `);return{wellKnownPath:r,metadata:JSON.stringify({resource:e.resource,authorization_servers:[e.issuer],scopes_supported:[...e.scopes],bearer_methods_supported:[`header`]}),challenges:{missing:`Bearer resource_metadata="${i}"`,invalid:`Bearer error="invalid_token", resource_metadata="${i}"`,scope:`Bearer error="insufficient_scope", scope="${a}", resource_metadata="${i}"`}}}function R(e,t,n){if(e.method!==`GET`){t.writeHead(405,{Allow:`GET`}).end();return}t.writeHead(200,{"Content-Type":`application/json`}).end(n.metadata)}function z(e,t,n,r){if(r.throttled)e.writeHead(429,{"Retry-After":String(r.retryAfterSeconds)}).end();else if(n===`missing-scope`)e.writeHead(403,{"WWW-Authenticate":t.challenges.scope}).end();else{let r=n===`missing-token`?t.challenges.missing:t.challenges.invalid;e.writeHead(401,{"WWW-Authenticate":r}).end()}}function B(e,t,n,r,i,a){if(i===`keys-unavailable`){a?.({refusal:i,remote:n.remote(e),throttled:!1}),t.writeHead(503).end();return}let o=n.fail(e);a?.({refusal:i,remote:o.remote,throttled:o.throttled}),z(t,r,i===`missing-token`||i===`missing-scope`?i:`invalid-token`,o)}const V=32*1024*1024,H=16*1024,U=16,W=/^[0-9a-f]{64}$/,G=/^[A-Za-z0-9+/]*={0,2}$/,K=3e4,q=new Set([`declined`,`too-large`,`bad-name`,`exists`,`unsafe-path`,`integrity`,`protocol`,`closed`,`timeout`,`unavailable`]),J=[[0,31],[127,159],[8203,8207],[8232,8238],[8294,8297],[65279,65279]];function te(e){let t=``;for(let n of e){let e=n.codePointAt(0)??0;t+=J.some(([t,n])=>e>=t&&e<=n)?` `:n}return t}function Y(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=0}function ne(e){if(e.length>22360)return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!=`object`||!t)return;let n=t;switch(n.t){case`file-offer`:{let{transferId:e,name:t,size:r,sha256:i}=n;return typeof e!=`string`||e.length===0||e.length>128||typeof t!=`string`||t.length>1024||!Y(r)||typeof i!=`string`||!W.test(i)?void 0:{t:`file-offer`,transferId:e,name:t,size:r,sha256:i}}case`file-accept`:return{t:`file-accept`};case`file-refuse`:{let e=n.reason,t=n.detail;return typeof e!=`string`||!q.has(e)?void 0:{t:`file-refuse`,reason:e,...typeof t==`string`?{detail:te(t).slice(0,500)}:{}}}case`file-chunk`:{let{seq:e,data:t}=n;return!Y(e)||typeof t!=`string`||t===``||!G.test(t)?void 0:{t:`file-chunk`,seq:e,data:t}}case`file-credit`:return Y(n.upTo)?{t:`file-credit`,upTo:n.upTo}:void 0;case`file-end`:return Y(n.chunks)?{t:`file-end`,chunks:n.chunks}:void 0;case`file-done`:return{t:`file-done`};default:return}}var X=class extends Error{reason;detail;constructor(e,t){super(t??e),this.reason=e,this.detail=t}},Z=class{capacity;queue=[];waiter;failure;stops=[];constructor(e,t){this.capacity=t,this.stops.push(e.onFrame(e=>{let t=ne(e);if(t===void 0){this.fail(new X(`protocol`,`a frame could not be read`));return}if(this.waiter!==void 0){let{resolve:e}=this.waiter;this.waiter=void 0,e(t);return}if(this.queue.length>=this.capacity){this.fail(new X(`protocol`,`the peer sent past its credit`));return}this.queue.push(t)}),e.onClose(()=>this.fail(new X(`closed`,`the channel closed`))))}fail(e){if(this.failure!==void 0)return;this.failure=e,e.reason===`protocol`&&(this.queue.length=0);let t=this.waiter;this.waiter=void 0,t?.reject(e)}next(e){let t=this.queue.shift();return t===void 0?this.failure===void 0?new Promise((t,n)=>{let r=setTimeout(()=>{this.waiter=void 0,n(new X(`timeout`,`nothing arrived within ${e}ms`))},e);r.unref?.(),this.waiter={resolve:e=>{clearTimeout(r),t(e)},reject:e=>{clearTimeout(r),n(e)}}}):Promise.reject(this.failure):Promise.resolve(t)}get ended(){return this.failure!==void 0}stop(){for(let e of this.stops.splice(0))e()}};function Q(e,t){try{e.send(JSON.stringify(t))}catch(e){throw new X(`closed`,e instanceof Error?e.message:String(e))}}function $(e){return e instanceof X?e:new X(`unavailable`,e instanceof Error?e.message:String(e))}async function*re(e,t){let n=Buffer.alloc(0);for await(let r of e)for(n=n.length===0?Buffer.from(r):Buffer.concat([n,r]);n.length>=t;)yield n.subarray(0,t),n=n.subarray(t);n.length>0&&(yield n)}async function ie(e){let{channel:t,offer:n,source:r}=e,i=e.chunkBytes??16384,a=e.window??16,o=e.idleMs??K,s=new Z(t,a+2);try{if(r.size!==n.size)throw new X(`integrity`,`the source is not the size offered`);Q(t,{t:`file-offer`,...n});let c=await s.next(e.decisionMs??3e5);if(c.t===`file-refuse`)throw new X(c.reason,c.detail);if(c.t!==`file-accept`)throw new X(`protocol`,`expected an answer`);let l=0,u=0,d=0,f=async()=>{let e=await s.next(o);if(e.t===`file-refuse`)throw new X(e.reason,e.detail);if(e.t!==`file-credit`||e.upTo<=u||e.upTo>l)throw new X(`protocol`,`expected credit`);u=e.upTo};for await(let e of re(r.read(),i)){if(d+=e.length,d>n.size)throw new X(`integrity`,`the source grew while sending`);for(;l-u>=a;)await f();Q(t,{t:`file-chunk`,seq:l,data:e.toString(`base64`)}),l+=1}if(d!==n.size)throw new X(`integrity`,`the source shrank while sending`);for(Q(t,{t:`file-end`,chunks:l});;){let e=await s.next(o);if(e.t===`file-done`)return{ok:!0};if(e.t===`file-refuse`)throw new X(e.reason,e.detail);if(e.t!==`file-credit`||e.upTo<=u||e.upTo>l)throw new X(`protocol`,`expected credit or completion`);u=e.upTo}}catch(e){let t=$(e);return{ok:!1,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail}}}finally{s.stop(),t.close()}}async function ae(t){let{channel:n}=t,r=t.maxBytes??33554432,i=t.window??16,a=t.idleMs??K,o=new Z(n,i+1),s=new AbortController,c=n.onClose(()=>s.abort()),l,u,d=!0;try{let i=await o.next(a);if(i.t!==`file-offer`)throw new X(`protocol`,`expected an offer`);if(l={transferId:i.transferId,name:i.name,size:i.size,sha256:i.sha256},l.size>r)throw new X(`too-large`,`${l.size} bytes is over the ${r}-byte limit`);let c=await t.admit(l,s.signal);if(`refused`in c)throw new X(c.refused,c.detail);if(u=c.sink,o.ended)throw new X(`closed`,`the sender left before the answer`);Q(n,{t:`file-accept`});let d=e(`sha256`),f=0,p=0;for(;;){let e=await o.next(a);if(e.t===`file-end`){if(e.chunks!==f||p!==l.size)throw new X(`integrity`,`the content is not the size offered`);if(d.digest(`hex`)!==l.sha256)throw new X(`integrity`,`the content does not match the offered sha256`);let t=await u.commit();return u=void 0,Q(n,{t:`file-done`}),{ok:!0,offer:l,location:t}}if(e.t!==`file-chunk`||e.seq!==f)throw new X(`protocol`,`expected the next chunk`);let t=Buffer.from(e.data,`base64`);if(p+=t.length,p>l.size)throw new X(`integrity`,`more content than offered`);d.update(t),await u.write(t),f+=1,Q(n,{t:`file-credit`,upTo:f})}}catch(e){let t=$(e);if(d=t.reason!==`protocol`,u!==void 0&&await u.discard().catch(()=>void 0),t.reason!==`closed`&&t.reason!==`timeout`)try{Q(n,{t:`file-refuse`,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail}})}catch{}return{ok:!1,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail},...l===void 0?{}:{offer:l}}}finally{d&&await oe(s.signal,5e3),c(),o.stop(),n.close()}}function oe(e,t){return e.aborted?Promise.resolve():new Promise(n=>{let r=setTimeout(i,t);r.unref?.(),e.addEventListener(`abort`,i,{once:!0});function i(){clearTimeout(r),e.removeEventListener(`abort`,i),n()}})}export{V as DEFAULT_MAX_FILE_BYTES,H as FILE_CHUNK_BYTES,U as FILE_CREDIT_WINDOW,f as bearerCredential,b as createAccessTokenVerifier,I as createBearerResourceServer,d as credentialMatches,L as describeProtectedResource,l as mintTransportToken,P as parsePublicHttpsUrl,ae as receiveFileOverChannel,B as refuseAccessToken,z as refuseBearerToken,u as resolveAdmission,p as sealHandoffRecord,ie as sendFileOverChannel,R as serveProtectedResourceMetadata,m as verifyHandoffPayload};
1
+ import{createHash as e,randomBytes as t,timingSafeEqual as n}from"node:crypto";import{decodeProtectedHeader as r,importJWK as i,jwtVerify as a}from"jose";import{fetchWithEgressPolicy as o}from"@robota-sdk/agent-core/node";import{BlockList as s,isIP as c}from"node:net";import{PassThrough as l}from"node:stream";function u(){let e=t(32).toString(`hex`);if(e.length!==64)throw Error(`transport admission: could not mint a credential of the required length`);return e}function d(e={}){let t=`open`in e&&e.open===!0,n=e.token!==void 0&&e.token!==null&&e.token!==``;if(n&&t)throw Error("transport admission: a token and `open: true` are contradictory — one requires a credential and the other requires none. Asking for both means one of the two beliefs behind the call is wrong; say which is intended.");if(n)return{token:e.token};if(e.token===null||t){let t=e.openReason?.trim();if(!t)throw Error("transport admission: an open transport requires `openReason`. An open transport is a decision, and a decision nobody wrote down is indistinguishable from an oversight.");return{token:null,openReason:t}}return{token:u()}}function f(e,t){if(t===void 0)return!1;let r=Buffer.from(e,`utf8`),i=Buffer.from(t,`utf8`);return r.length===i.length&&n(r,i)}function p(e){if(!e||e.slice(0,6).toLowerCase()!==`bearer`)return;let t=e.slice(6);if(t===``||t[0]!==` `&&t[0]!==` `)return;let n=t.replace(/^[ \t]+/,``);return n===``?void 0:n}function m(t){let n=JSON.stringify(t),r=Buffer.from(n,`utf8`);return{serialized:n,integrity:{digest:e(`sha256`).update(r).digest(`base64url`),byteLength:r.byteLength}}}function h(t,n){let r=Buffer.from(t,`utf8`);return r.byteLength===n.byteLength?e(`sha256`).update(r).digest(`base64url`)===n.digest?{intact:!0}:{intact:!1,failure:`digest-mismatch`}:{intact:!1,failure:`truncated`,expectedBytes:n.byteLength,actualBytes:r.byteLength}}const g=10*6e4,_=`at+jwt`,v=[`RS256`,`ES256`,`EdDSA`],y={RS256:{kty:`RSA`},ES256:{kty:`EC`,crv:`P-256`},EdDSA:{kty:`OKP`,crv:`Ed25519`}};var b=class{refusal;constructor(e){this.refusal=e}};function x(e,t={}){let n=S(e),s=t.now??Date.now,c=t.monotonicNow??(()=>performance.now()),l={fetch:t.fetch,lookup:t.lookup},u=n.hostname.replace(/^\[|\]$/g,``).toLowerCase(),d={keys:void 0,jwksUri:void 0,fetchedAt:void 0,lastAttemptAt:void 0,inflight:void 0},f=new Set(e.allowedSubjects??[]),p=new Set(e.allowedClients??[]);async function m(e){if(new URL(e).protocol!==`https:`)return;let t=await o(e,{headers:{accept:`application/json`},timeoutMs:1e4,maxResponseBytes:65536},{allowedHosts:[u],maxRedirects:0},l);if(!(!t.ok||t.status!==200))return JSON.parse(new TextDecoder().decode(t.body))}async function h(){for(let t of C(n)){let n=await m(t).catch(()=>void 0);if(!D(n))continue;if(n.issuer!==e.issuer)return;let r=n.jwks_uri;return typeof r!=`string`||!URL.canParse(r)?void 0:new URL(r).protocol===`https:`?r:void 0}}function v(){return d.fetchedAt===void 0?1/0:c()-d.fetchedAt}async function y(){d.lastAttemptAt=c();try{if(v()>=g&&(d.jwksUri=void 0),d.jwksUri??=await h(),d.jwksUri===void 0)return;let e=w(await m(d.jwksUri));if(e===void 0)return;d.keys=e,d.fetchedAt=c()}catch{}}async function x(){if(d.inflight!==void 0)return d.inflight;if(!(d.lastAttemptAt!==void 0&&c()-d.lastAttemptAt<3e4))return d.inflight=y().finally(()=>{d.inflight=void 0}),d.inflight}async function O(){return v()>=g&&await x(),v()>=36e5&&(d.keys=void 0,d.jwksUri=void 0,d.fetchedAt=void 0),d.keys}async function k(e,t){let n=await O();if(n===void 0)throw new b(`keys-unavailable`);if(e===void 0){if(n.length>1)throw new b(`ambiguous-key`);let e=n[0];if(e===void 0)throw new b(`unknown-key`);if(!T(e,t))throw new b(`key-mismatch`);return e}let r=n.filter(t=>t.kid===e);if(r.length===0&&(await x(),n=d.keys??[],r=n.filter(t=>t.kid===e)),r.length===0)throw new b(`unknown-key`);let i=r.find(e=>T(e,t));if(i===void 0)throw new b(`key-mismatch`);return i}async function A(t){if(typeof t!=`string`||t.length===0)throw new b(`malformed`);if(t.length>8192)throw new b(`oversize`);let n;try{n=r(t)}catch{throw new b(`malformed`)}if(typeof n.typ!=`string`||E(n.typ)!==_)throw new b(`wrong-type`);let o=e.algorithms.find(e=>e===n.alg);if(o===void 0)throw new b(`unsupported-algorithm`);if(n.kid!==void 0&&typeof n.kid!=`string`)throw new b(`malformed`);let c=await k(n.kid,o),l;try{l=await i(c,o)}catch{throw new b(`key-mismatch`)}let u;try{({payload:u}=await a(t,l,{issuer:e.issuer,audience:e.resource,algorithms:[o],typ:_,clockTolerance:60,currentDate:new Date(s()),requiredClaims:[`exp`,`sub`,`client_id`]}))}catch(e){throw new b(ee(e))}let d=typeof u.scope==`string`?u.scope.split(` `):[];if(!e.requiredScopes.every(e=>d.includes(e)))throw new b(`missing-scope`);let m=u.sub,h=u.client_id;if(!(typeof m==`string`&&f.has(m)||typeof h==`string`&&p.has(h)))throw new b(`principal-not-allowed`);return{admitted:!0}}return{async verify(e){try{return await A(e)}catch(e){return{admitted:!1,refusal:e instanceof b?e.refusal:`malformed`}}}}}function S(e){if(!URL.canParse(e.issuer))throw Error(`access-token verifier: issuer is not a URL`);let t=new URL(e.issuer);if(t.protocol!==`https:`||t.search!==``||t.hash!==``)throw Error(`access-token verifier: issuer must be an https URL with no query or fragment`);if(!URL.canParse(e.resource))throw Error(`access-token verifier: resource is not a URL`);if(e.algorithms.length===0||!e.algorithms.every(e=>v.includes(e)))throw Error(`access-token verifier: algorithms must be a non-empty subset of RS256, ES256, EdDSA`);if(e.requiredScopes.length===0)throw Error(`access-token verifier: at least one required scope is needed`);if((e.allowedSubjects?.length??0)+(e.allowedClients?.length??0)===0)throw Error(`access-token verifier: name at least one allowed subject or client — this host is single-tenant, and issuer + audience + scope alone admit anyone the issuer serves`);return t}function C(e){let t=e.pathname===`/`?``:e.pathname.replace(/\/$/,``);return[`${e.origin}/.well-known/oauth-authorization-server${t}`,`${e.origin}${t}/.well-known/openid-configuration`]}function w(e){if(!(!D(e)||!Array.isArray(e.keys)||e.keys.length>32))return e.keys.filter(e=>D(e)&&typeof e.kty==`string`)}function T(e,t){let n=y[t];return e.kty!==n.kty||n.crv!==void 0&&e.crv!==n.crv||e.alg!==void 0&&e.alg!==t||e.use!==void 0&&e.use!==`sig`||e.key_ops!==void 0&&!e.key_ops.includes(`verify`)?!1:e.d===void 0}function ee(e){let t=D(e)?e.code:void 0,n=D(e)?e.claim:void 0;switch(t){case`ERR_JWT_EXPIRED`:return`expired`;case`ERR_JWS_SIGNATURE_VERIFICATION_FAILED`:return`bad-signature`;case`ERR_JOSE_ALG_NOT_ALLOWED`:return`unsupported-algorithm`;case`ERR_JWT_CLAIM_VALIDATION_FAILED`:return n===`iss`?`wrong-issuer`:n===`aud`?`wrong-audience`:n===`nbf`?`not-yet-valid`:n===`typ`?`wrong-type`:`malformed`;default:return`malformed`}}function E(e){let t=e.toLowerCase();return t.startsWith(`application/`)?t.slice(12):t}function D(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}const O=6e4,k=4096,A=/^[\x21\x23-\x5b\x5d-\x7e]+$/,j=new s;j.addSubnet(`127.0.0.0`,8,`ipv4`),j.addAddress(`::1`,`ipv6`);const M=new s;M.addSubnet(`10.0.0.0`,8,`ipv4`),M.addSubnet(`172.16.0.0`,12,`ipv4`),M.addSubnet(`192.168.0.0`,16,`ipv4`),M.addSubnet(`169.254.0.0`,16,`ipv4`),M.addSubnet(`100.64.0.0`,10,`ipv4`),M.addSubnet(`fc00::`,7,`ipv6`),M.addSubnet(`fe80::`,10,`ipv6`);function N(e){if(e===void 0)return;let t=e.trim().toLowerCase(),n=t.startsWith(`::ffff:`)?t.slice(7):void 0;return n!==void 0&&c(n)===4?n:c(t)===0?void 0:t}function P(e){if(e===void 0)return`unknown`;let t=c(e)===4?`ipv4`:`ipv6`;return j.check(e,t)?`loopback`:M.check(e,t)?`private`:`public`}function F(e,t){let n;try{n=new URL(e)}catch{throw Error(`${t} public URL must be an absolute https URL`)}if(n.protocol!==`https:`)throw Error(`${t} public URL must use https`);if(n.username!==``||n.password!==``||n.search!==``||n.hash!==``)throw Error(`${t} public URL must not carry credentials, a query or a fragment`);return n}function te(e){let t=new Map;return n=>{let r=e(),i=t.get(n);if(i===void 0||r-i.startedAt>=O){if(t.delete(n),t.size>=k){for(let[e,n]of t)r-n.startedAt>=O&&t.delete(e);let e=t.keys().next();t.size>=k&&e.done!==!0&&t.delete(e.value)}i={count:0,startedAt:r},t.set(n,i)}i.count+=1;let a=O-(r-i.startedAt);return{throttled:i.count>20,retryAfterSeconds:Math.max(1,Math.ceil(a/1e3))}}}function ne(e){let t=F(e.publicUrl,e.label),n=new Set;for(let t of e.trustedProxies??[]){let r=N(t);if(r===void 0)throw Error(`${e.label} trusted proxy must be a literal IP address`);n.add(r)}let r=new Set([t.host]);t.port===``&&r.add(`${t.hostname}:443`);let i=te(e.now??(()=>performance.now()));function a(e){let t=N(e.socket.remoteAddress);if(t===void 0||!n.has(t))return t;let r=e.headers[`x-forwarded-for`],i=(Array.isArray(r)?r.join(`,`):r??``).split(`,`);for(let e=i.length-1;e>=0;--e){let r=N(i[e]);if(r===void 0)return t;if(!n.has(r))return r}return t}return{url:t,checkNames(e,n){let i=e.headers.host?.toLowerCase(),a=e.headers.origin;return i===void 0||!r.has(i)||a!==void 0&&a!==t.origin?(n.writeHead(403).end(),!1):!0},fail(e){let t=a(e);return{remote:P(t),...i(t??`unknown`)}},remote(e){return P(a(e))}}}function re(e){let t=F(e.resource,e.label),n;try{n=new URL(e.issuer)}catch{throw Error(`${e.label} authorization issuer must be an absolute https URL`)}if(n.protocol!==`https:`)throw Error(`${e.label} authorization issuer must use https`);if(e.scopes.length===0||!e.scopes.every(e=>A.test(e)))throw Error(`${e.label} authorization needs at least one valid scope`);let r=`/.well-known/oauth-protected-resource${t.pathname===`/`?``:t.pathname}`,i=`${t.origin}${r}`,a=e.scopes.join(` `);return{wellKnownPath:r,metadata:JSON.stringify({resource:e.resource,authorization_servers:[e.issuer],scopes_supported:[...e.scopes],bearer_methods_supported:[`header`]}),challenges:{missing:`Bearer resource_metadata="${i}"`,invalid:`Bearer error="invalid_token", resource_metadata="${i}"`,scope:`Bearer error="insufficient_scope", scope="${a}", resource_metadata="${i}"`}}}function I(e,t,n){if(e.method!==`GET`){t.writeHead(405,{Allow:`GET`}).end();return}t.writeHead(200,{"Content-Type":`application/json`}).end(n.metadata)}function L(e,t,n,r){if(r.throttled)e.writeHead(429,{"Retry-After":String(r.retryAfterSeconds)}).end();else if(n===`missing-scope`)e.writeHead(403,{"WWW-Authenticate":t.challenges.scope}).end();else{let r=n===`missing-token`?t.challenges.missing:t.challenges.invalid;e.writeHead(401,{"WWW-Authenticate":r}).end()}}function R(e,t,n,r,i,a){if(i===`keys-unavailable`){a?.({refusal:i,remote:n.remote(e),throttled:!1}),t.writeHead(503).end();return}let o=n.fail(e);a?.({refusal:i,remote:o.remote,throttled:o.throttled}),L(t,r,i===`missing-token`||i===`missing-scope`?i:`invalid-token`,o)}const z=32*1024*1024,B=16*1024,V=16,H=/^[0-9a-f]{64}$/,U=/^[A-Za-z0-9+/]*={0,2}$/,W=3e4,G=new Set([`declined`,`too-large`,`bad-name`,`exists`,`unsafe-path`,`integrity`,`protocol`,`closed`,`timeout`,`unavailable`]),K=[[0,31],[127,159],[8203,8207],[8232,8238],[8294,8297],[65279,65279]];function q(e){let t=``;for(let n of e){let e=n.codePointAt(0)??0;t+=K.some(([t,n])=>e>=t&&e<=n)?` `:n}return t}function J(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=0}function Y(e){if(e.length>22360)return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!=`object`||!t)return;let n=t;switch(n.t){case`file-offer`:{let{transferId:e,name:t,size:r,sha256:i}=n;return typeof e!=`string`||e.length===0||e.length>128||typeof t!=`string`||t.length>1024||!J(r)||typeof i!=`string`||!H.test(i)?void 0:{t:`file-offer`,transferId:e,name:t,size:r,sha256:i}}case`file-accept`:return{t:`file-accept`};case`file-refuse`:{let e=n.reason,t=n.detail;return typeof e!=`string`||!G.has(e)?void 0:{t:`file-refuse`,reason:e,...typeof t==`string`?{detail:q(t).slice(0,500)}:{}}}case`file-chunk`:{let{seq:e,data:t}=n;return!J(e)||typeof t!=`string`||t===``||!U.test(t)?void 0:{t:`file-chunk`,seq:e,data:t}}case`file-credit`:return J(n.upTo)?{t:`file-credit`,upTo:n.upTo}:void 0;case`file-end`:return J(n.chunks)?{t:`file-end`,chunks:n.chunks}:void 0;case`file-done`:return{t:`file-done`};default:return}}var X=class extends Error{reason;detail;constructor(e,t){super(t??e),this.reason=e,this.detail=t}},Z=class{capacity;queue=[];waiter;failure;stops=[];constructor(e,t){this.capacity=t,this.stops.push(e.onFrame(e=>{let t=Y(e);if(t===void 0){this.fail(new X(`protocol`,`a frame could not be read`));return}if(this.waiter!==void 0){let{resolve:e}=this.waiter;this.waiter=void 0,e(t);return}if(this.queue.length>=this.capacity){this.fail(new X(`protocol`,`the peer sent past its credit`));return}this.queue.push(t)}),e.onClose(()=>this.fail(new X(`closed`,`the channel closed`))))}fail(e){if(this.failure!==void 0)return;this.failure=e,e.reason===`protocol`&&(this.queue.length=0);let t=this.waiter;this.waiter=void 0,t?.reject(e)}next(e){let t=this.queue.shift();return t===void 0?this.failure===void 0?new Promise((t,n)=>{let r=setTimeout(()=>{this.waiter=void 0,n(new X(`timeout`,`nothing arrived within ${e}ms`))},e);r.unref?.(),this.waiter={resolve:e=>{clearTimeout(r),t(e)},reject:e=>{clearTimeout(r),n(e)}}}):Promise.reject(this.failure):Promise.resolve(t)}get ended(){return this.failure!==void 0}stop(){for(let e of this.stops.splice(0))e()}};function Q(e,t){try{e.send(JSON.stringify(t))}catch(e){throw new X(`closed`,e instanceof Error?e.message:String(e))}}function $(e){return e instanceof X?e:new X(`unavailable`,e instanceof Error?e.message:String(e))}async function*ie(e,t){let n=Buffer.alloc(0);for await(let r of e)for(n=n.length===0?Buffer.from(r):Buffer.concat([n,r]);n.length>=t;)yield n.subarray(0,t),n=n.subarray(t);n.length>0&&(yield n)}async function ae(e){let{channel:t,offer:n,source:r}=e,i=e.chunkBytes??16384,a=e.window??16,o=e.idleMs??W,s=new Z(t,a+2);try{if(r.size!==n.size)throw new X(`integrity`,`the source is not the size offered`);Q(t,{t:`file-offer`,...n});let c=await s.next(e.decisionMs??3e5);if(c.t===`file-refuse`)throw new X(c.reason,c.detail);if(c.t!==`file-accept`)throw new X(`protocol`,`expected an answer`);let l=0,u=0,d=0,f=async()=>{let e=await s.next(o);if(e.t===`file-refuse`)throw new X(e.reason,e.detail);if(e.t!==`file-credit`||e.upTo<=u||e.upTo>l)throw new X(`protocol`,`expected credit`);u=e.upTo};for await(let e of ie(r.read(),i)){if(d+=e.length,d>n.size)throw new X(`integrity`,`the source grew while sending`);for(;l-u>=a;)await f();Q(t,{t:`file-chunk`,seq:l,data:e.toString(`base64`)}),l+=1}if(d!==n.size)throw new X(`integrity`,`the source shrank while sending`);for(Q(t,{t:`file-end`,chunks:l});;){let e=await s.next(o);if(e.t===`file-done`)return{ok:!0};if(e.t===`file-refuse`)throw new X(e.reason,e.detail);if(e.t!==`file-credit`||e.upTo<=u||e.upTo>l)throw new X(`protocol`,`expected credit or completion`);u=e.upTo}}catch(e){let t=$(e);return{ok:!1,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail}}}finally{s.stop(),t.close()}}async function oe(t){let{channel:n}=t,r=t.maxBytes??33554432,i=t.window??16,a=t.idleMs??W,o=new Z(n,i+1),s=new AbortController,c=n.onClose(()=>s.abort()),l,u,d=!0;try{let i=await o.next(a);if(i.t!==`file-offer`)throw new X(`protocol`,`expected an offer`);if(l={transferId:i.transferId,name:i.name,size:i.size,sha256:i.sha256},l.size>r)throw new X(`too-large`,`${l.size} bytes is over the ${r}-byte limit`);let c=await t.admit(l,s.signal);if(`refused`in c)throw new X(c.refused,c.detail);if(u=c.sink,o.ended)throw new X(`closed`,`the sender left before the answer`);Q(n,{t:`file-accept`});let d=e(`sha256`),f=0,p=0;for(;;){let e=await o.next(a);if(e.t===`file-end`){if(e.chunks!==f||p!==l.size)throw new X(`integrity`,`the content is not the size offered`);if(d.digest(`hex`)!==l.sha256)throw new X(`integrity`,`the content does not match the offered sha256`);let t=await u.commit();return u=void 0,Q(n,{t:`file-done`}),{ok:!0,offer:l,location:t}}if(e.t!==`file-chunk`||e.seq!==f)throw new X(`protocol`,`expected the next chunk`);let t=Buffer.from(e.data,`base64`);if(p+=t.length,p>l.size)throw new X(`integrity`,`more content than offered`);d.update(t),await u.write(t),f+=1,Q(n,{t:`file-credit`,upTo:f})}}catch(e){let t=$(e);if(d=t.reason!==`protocol`,u!==void 0&&await u.discard().catch(()=>void 0),t.reason!==`closed`&&t.reason!==`timeout`)try{Q(n,{t:`file-refuse`,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail}})}catch{}return{ok:!1,reason:t.reason,...t.detail===void 0?{}:{detail:t.detail},...l===void 0?{}:{offer:l}}}finally{d&&await se(s.signal,5e3),c(),o.stop(),n.close()}}function se(e,t){return e.aborted?Promise.resolve():new Promise(n=>{let r=setTimeout(i,t);r.unref?.(),e.addEventListener(`abort`,i,{once:!0});function i(){clearTimeout(r),e.removeEventListener(`abort`,i),n()}})}function ce(e,t,n){let r=new l,i=!1;return e.once(`end`,n.onInputClose),e.once(`close`,n.onInputClose),t.once(`close`,n.onOutputClose),t.on(`error`,n.onOutputError),e.pipe(r),{bridge:r,release(){i||(i=!0,e.off(`end`,n.onInputClose),e.off(`close`,n.onInputClose),t.off(`close`,n.onOutputClose),t.off(`error`,n.onOutputError),e.unpipe(r),r.destroy())}}}export{z as DEFAULT_MAX_FILE_BYTES,B as FILE_CHUNK_BYTES,V as FILE_CREDIT_WINDOW,ce as attachStdioCarrier,p as bearerCredential,x as createAccessTokenVerifier,ne as createBearerResourceServer,f as credentialMatches,re as describeProtectedResource,u as mintTransportToken,F as parsePublicHttpsUrl,oe as receiveFileOverChannel,R as refuseAccessToken,L as refuseBearerToken,d as resolveAdmission,m as sealHandoffRecord,ae as sendFileOverChannel,I as serveProtectedResourceMetadata,h as verifyHandoffPayload};
2
2
  //# sourceMappingURL=node.js.map