UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

2 lines 7.81 kB
import{spawn as e}from"node:child_process";import{readFile as t}from"node:fs/promises";import n from"node:path";function r(e,t=`ERR_SECRETS_STORE`){return Object.assign(Error(e),{code:t})}function i(t,n,{input:i=``,timeout:a=6e4,maxOutput:o=1048576}={}){return new Promise((s,c)=>{let l;try{l=e(t,n,{shell:!1,windowsHide:!0,stdio:[`pipe`,`pipe`,`pipe`],cwd:void 0})}catch{c(r(`Could not start the OS credential tool.`,`ERR_SECRETS_UNAVAILABLE`));return}let u=[],d=[],f=0,p,m,h=!1,g=(e,t)=>{if(!h){h=!0,clearTimeout(v);for(let e of[...u,...d])e.fill(0);u.length=d.length=0,e?c(e):s(t)}},_=e=>{p??=e,l.kill(`SIGKILL`),l.stdin.destroy(),l.stdout.destroy(),l.stderr.destroy(),g(p)},v=setTimeout(()=>_(r(`The OS credential tool timed out. Unlock your credential store and retry.`,`ERR_SECRETS_TIMEOUT`)),a);for(let[e,t]of[[l.stdout,u],[l.stderr,d]])e.on(`data`,e=>{if(h){e.fill(0);return}f+=e.length,f>o?(e.fill(0),_(r(`The OS credential tool exceeded its output limit.`,`ERR_SECRETS_OUTPUT_LIMIT`))):t.push(e)}),e.on(`error`,()=>_(r(`Could not read the OS credential tool response.`)));l.on(`error`,e=>{let t=e.code===`EACCES`||e.code===`EPERM`?`ERR_SECRETS_ACCESS_DENIED`:`ERR_SECRETS_UNAVAILABLE`;g(r(`Could not start the OS credential tool. Check its installation and permissions.`,t))}),l.stdin.on(`error`,()=>{m=r(`Could not send input to the OS credential tool.`)}),l.on(`close`,(e,t)=>{if(h)return;if(t!==null||!Number.isInteger(e)){g(r(`The OS credential tool terminated unexpectedly.`));return}if(e===0&&m){g(m);return}let n=Buffer.concat(u),i=Buffer.concat(d),a={code:e,stdout:n.toString(`utf8`),stderr:i.toString(`utf8`)};n.fill(0),i.fill(0),g(void 0,a)}),l.stdin.end(i)})}const a=`/usr/bin/security`,o=`secrets:v1:`,s=e=>o+Buffer.from(e,`utf8`).toString(`base64`),c=e=>`"`+e.replaceAll(`\\`,`\\\\`).replaceAll(`"`,`\\"`)+`"`,l=`macOS Keychain operation failed. Check Keychain access and unlock your login keychain.`;function u({run:e=i,keychain:t=`login.keychain-db`}={}){if(/[\r\n\0]/u.test(t))throw TypeError(`Invalid keychain path.`);let n=(e,t)=>[`-s`,s(e),`-a`,s(t)];function u(e,t=!1){if(t&&e.code===44)return!1;if(e.code!==0){let t=e.code===36||e.code===128?`ERR_SECRETS_ACCESS_DENIED`:`ERR_SECRETS_STORE`;throw r(l,t)}return!0}let d=async()=>u(await e(a,[`-q`,`show-keychain-info`,t]));return{async get(i,s){await d();let c=await e(a,[`-q`,`find-generic-password`,...n(i,s),`-w`,t]);if(!u(c,!0))return null;let f=c.stdout.replace(/\n$/u,``);if(!f.startsWith(o))throw r(l);let p=f.slice(11),m=Buffer.from(p,`base64`),h=m.toString(`utf8`);if(m.length>2560||m.toString(`base64`)!==p||!Buffer.from(h,`utf8`).equals(m))throw r(l);return h},async set(i,o,f){let p=[`add-generic-password`,`-U`,...n(i,o),`-w`,s(f),t].map(c).join(` `)+` `;if(Buffer.byteLength(p)>=4096)throw RangeError(`The combined service, name, and value exceed the macOS command limit.`);await d();let m=await e(a,[`-q`,`-i`],{input:p});if(u(m),m.stderr!==``||m.stdout!==``)throw r(l)},async delete(r,i){return await d(),u(await e(a,[`-q`,`delete-generic-password`,...n(r,i),t]),!0)}}}const d=`Linux credential operation failed; ensure /usr/bin/secret-tool and an unlocked Secret Service session are available`,f=`secrets:v1:`,p=new Set([`ERR_SECRETS_UNAVAILABLE`,`ERR_SECRETS_TIMEOUT`,`ERR_SECRETS_OUTPUT_LIMIT`,`ERR_SECRETS_STORE`,`ERR_SECRETS_ACCESS_DENIED`]);function m(e){return f+Buffer.from(e,`utf8`).toString(`base64`)}function h(e){if(!e.startsWith(f)||e.length>3427)throw r(d);let t=e.slice(11),n=Buffer.from(t,`base64`),i=n.toString(`utf8`);if(n.length>2560||n.toString(`base64`)!==t||!Buffer.from(i,`utf8`).equals(n))throw r(d);return i}function g({run:e=i}={}){async function t(t,n,i,a){let o=[`application`,`secrets`,`service`,m(n),`name`,m(i)],s;try{s=await e(`/usr/bin/secret-tool`,[t,...t===`store`?[`--label=secrets`]:t===`search`?[`--all`,`--unlock`]:[],`--`,...o],{input:t===`store`?m(a):``})}catch(e){let t=p.has(e?.code)?e.code:e?.code===`ENOENT`?`ERR_SECRETS_UNAVAILABLE`:e?.code===`EACCES`||e?.code===`EPERM`?`ERR_SECRETS_ACCESS_DENIED`:`ERR_SECRETS_STORE`;throw r(d,t)}if(!s||!Number.isInteger(s.code)||typeof s.stdout!=`string`||typeof s.stderr!=`string`||s.code!==0)throw r(d);if(t===`search`){let e=new Set([0,2,4].map(e=>`attribute.${o[e]} = ${o[e+1]}\n`));if((s.stderr.match(/[^\n]*\n|[^\n]+$/g)??[]).some(t=>!e.has(t)))throw r(d);if(s.stdout===``&&s.stderr===``)return!1;let t=/\[[^\r\n\]]+\]\nlabel = [^\r\n]*\nsecret = (secrets:v1:[A-Za-z0-9+/=]*)\ncreated = [^\r\n]*\nmodified = [^\r\n]*\n(?:schema = [^\r\n]*\n)?/gy,n=0,i;for(;(i=t.exec(s.stdout))!==null;)h(i[1]),n=t.lastIndex;if(n===0||n!==s.stdout.length)throw r(d);return!0}if(s.stderr!==``||t!==`lookup`&&s.stdout!==``)throw r(d);return t!==`lookup`||h(s.stdout)}return{async get(e,n){return await t(`search`,e,n)?t(`lookup`,e,n):null},async set(e,n,r){await t(`store`,e,n,r)},async delete(e,n){if(!await t(`search`,e,n))return!1;if(await t(`clear`,e,n),await t(`search`,e,n))throw r(d);return!0}}}const _=new URL(`./windows.ps1`,import.meta.url),v=`Windows credential operation failed`,y=new Set([`ERR_SECRETS_UNAVAILABLE`,`ERR_SECRETS_TIMEOUT`,`ERR_SECRETS_OUTPUT_LIMIT`,`ERR_SECRETS_STORE`,`ERR_SECRETS_ACCESS_DENIED`]);function b({run:e=i}={}){let a;async function o(i,o,s,c){let l=process.env.SystemRoot??process.env.SYSTEMROOT;if(typeof l!=`string`||!/^[a-z]:\\/i.test(l)||l.includes(`\0`))throw r(`Windows PowerShell is unavailable`,`ERR_SECRETS_UNAVAILABLE`);let u=n.win32.join(l,`System32`,`WindowsPowerShell`,`v1.0`,`powershell.exe`),d;try{a??=t(_,`utf8`).then(e=>Buffer.from(e,`utf16le`).toString(`base64`)),d=await e(u,[`-NoLogo`,`-NoProfile`,`-NonInteractive`,`-InputFormat`,`Text`,`-OutputFormat`,`Text`,`-EncodedCommand`,await a],{input:JSON.stringify({operation:i,service:o,name:s,...i===`set`?{value:c}:{}})})}catch(e){throw r(v,y.has(e?.code)?e.code:`ERR_SECRETS_STORE`)}let f;try{if(!d||!Number.isInteger(d.code)||typeof d.stdout!=`string`||(f=JSON.parse(d.stdout),!f||typeof f!=`object`||Array.isArray(f)||Object.keys(f).length!==1))throw null}catch{throw r(v)}if(d.code!==0){if(f.error===1168&&i!==`set`)return i===`get`&&null;let e=f.error===5?`ERR_SECRETS_ACCESS_DENIED`:f.error===1312||f.error===50||f.error===`ERR_SECRETS_UNAVAILABLE`?`ERR_SECRETS_UNAVAILABLE`:`ERR_SECRETS_STORE`;throw r(v,e)}if(d.stderr!==``||!Object.hasOwn(f,`value`))throw r(v);if(i===`get`){if(f.value!==null&&(typeof f.value!=`string`||!f.value.isWellFormed()||Buffer.byteLength(f.value,`utf8`)>2560))throw r(v);return f.value}if(i===`delete`&&typeof f.value==`boolean`)return f.value;if(i!==`set`||f.value!==!0)throw r(v)}return{get:(e,t)=>o(`get`,e,t),set:(e,t,n)=>o(`set`,e,t,n),delete:(e,t)=>o(`delete`,e,t)}}function x(e,t,n){if(typeof e!=`string`)throw TypeError(`${t} must be a string.`);if(!e.isWellFormed())throw TypeError(`${t} must contain valid Unicode.`);if(t!==`value`&&e.length===0)throw TypeError(`${t} must not be empty.`);if(Buffer.byteLength(e,`utf8`)>n)throw RangeError(`${t} must not exceed ${n} UTF-8 bytes.`);return e}function S(e,t){let n=e[0]!==null&&typeof e[0]==`object`&&!Array.isArray(e[0]);if(e.length!==(n?1:t?3:2))throw TypeError(`Expected an options object or service, name`+(t?`, value.`:`.`));let r=n?e[0]:{service:e[0],name:e[1],value:e[2]};return[x(r.service,`service`,256),x(r.name,`name`,256),...t?[x(r.value,`value`,2560)]:[]]}function C(){switch(process.platform){case`darwin`:return u();case`linux`:return g();case`win32`:return b();default:throw r(`Native secret storage is supported on macOS, Linux, and Windows.`,`ERR_SECRETS_UNSUPPORTED`)}}function w(e=C){let t,n=()=>t??=e();return Object.freeze({async get(...e){let t=S(e,!1);return n().get(...t)},async set(...e){let t=S(e,!0);await n().set(...t)},async delete(...e){let t=S(e,!1);return n().delete(...t)}})}const T=w();export{T as secrets};