eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
1 lines • 7.23 kB
JavaScript
import{optionById,optionByValue,requiredOptionId}from"./question-options.js";import{renderEditableQuestion}from"./ask-editable.js";import{InteractionRequired,InvalidAnswerError,SkippedSignal}from"./ask-signals.js";import{SetupPrerequisiteRequired}from"./integrations/shared/prerequisite.js";const select=e=>({...e,kind:`select`}),confirm=e=>({...e,kind:`confirm`});function text(e){return{...e,kind:`text`}}function announce(e,t,n,r){return e?.onResolved?.({key:t,value:n,source:r}),n}function coerceAnswer(e,t){if(e.kind===`select`){let n=String(t),r=optionById(e,n);if(!r){let t=e.options.map(e=>e.id).join(`, `);throw new InvalidAnswerError(e,`Invalid answer for "${e.key}": ${n}. Expected one of: ${t}.`)}return r.value}if(e.kind===`confirm`){let n=typeof t==`boolean`?t:t===`true`||t!==`false`&&null;if(n===null)throw new InvalidAnswerError(e,`Invalid answer for "${e.key}": expected a boolean.`);return n}let n=String(t),r=e.validate?.(n);if(r)throw new InvalidAnswerError(e,r);return n}function coerceManyAnswer(e,t){if(!Array.isArray(t))throw new InvalidAnswerError(e,`Invalid answer for "${e.key}": expected an array of option ids.`);let n=[];for(let r of t){let t=String(r),i=optionById(e,t);if(!i){let n=e.options.map(e=>e.id).join(`, `);throw new InvalidAnswerError(e,`Invalid answer for "${e.key}": ${t}. Expected one of: ${n}.`)}if(i.disabled){let n=i.disabledReason===void 0?``:` (${i.disabledReason})`;throw new InvalidAnswerError(e,`Invalid answer for "${e.key}": ${t} is unavailable${n}.`)}n.includes(i.value)||n.push(i.value)}for(let t of e.options)t.locked===!0&&!n.includes(t.value)&&n.push(t.value);return n}async function renderQuestion(e,t){if(t.kind===`select`){let n=t.detected??t.recommended;return coerceAnswer(t,await e.select({message:t.message,options:t.options.map(e=>({value:e.id,label:e.label,hint:e.hint,featured:e.featured})),initialValue:n===void 0?void 0:requiredOptionId(t,n,t.detected===void 0?`recommendation`:`detected value`),search:t.search,placeholder:t.placeholder}))}if(t.kind===`confirm`){let n=t.detected??t.recommended;return coerceAnswer(t,await e.select({message:t.message,options:[{value:`yes`,label:`Yes`},{value:`no`,label:`No`}],initialValue:n===void 0?void 0:n?`yes`:`no`})===`yes`)}let n=t.validate,r=n===void 0?void 0:e=>n(e)??void 0,i=t.detected??t.recommended;return coerceAnswer(t,t.sensitive?await e.password({message:t.message,validate:r}):await e.text({message:t.message,placeholder:t.placeholder,defaultValue:i===void 0?void 0:String(i),validate:r}))}async function renderManyQuestion(e,t){let n=t.detected??t.recommended;return(await e.select({multiple:!0,message:t.message,options:t.options.map(e=>({value:e.id,label:e.label,hint:e.hint,featured:e.featured,disabled:e.disabled,disabledReason:e.disabledReason,locked:e.locked,lockedReason:e.lockedReason})),initialValues:n===void 0?void 0:n.map(e=>requiredOptionId(t,e,t.detected===void 0?`recommendation`:`detected value`)),required:t.requireSelection,search:t.search,placeholder:t.placeholder})).map(e=>{let n=optionById(t,e);if(!n)throw new InvalidAnswerError(t,`Invalid answer for "${t.key}": ${e} is not an option id.`);return n.value})}function interactiveAsker(e,t){return{async ask(n){let r=await renderQuestion(e,n);return n.internal?r:announce(t,n.key,r,`asked`)},async askEditable(n){let r=await renderEditableQuestion(e,n);return announce(t,n.key,r.value,`asked`),r.text!==void 0&&announce(t,n.editable.key,r.text,`asked`),r},async askMany(n){let r=await renderManyQuestion(e,n);return n.internal?r:announce(t,n.key,r,`asked`)}}}function headlessAsker(e){function refuse(t){throw t.required?new InteractionRequired(t):(announce(e,t.key,void 0,`skipped`),new SkippedSignal(t.key))}return{async ask(e){return refuse(e)},async askEditable(e){return refuse(e)},async askMany(e){return refuse(e)}}}function withAnswers(e,t){return n=>({async ask(r){if(r.key in e){let n=coerceAnswer(r,e[r.key]);return announce(t,r.key,n,`answer`)}if(r.kind===`text`&&r.sensitive===!0){let e=process.env[r.environment];if(e!==void 0)return coerceAnswer(r,e);try{return await n.ask(r)}catch(e){throw e instanceof InteractionRequired?new SetupPrerequisiteRequired({kind:`environment`,code:r.key,message:`Set ${r.environment}, then retry setup.`,variable:r.environment,sensitive:!0}):e}}return n.ask(r)},async askEditable(r){if(!(r.key in e))return n.askEditable(r);let i=coerceAnswer(select({key:r.key,message:r.message,options:r.options}),e[r.key]);if(announce(t,r.key,i,`answer`),i!==r.editable.value)return{value:i};if(!(r.editable.key in e))return{value:i,text:(await n.ask(text({key:r.editable.key,message:optionByValue(r,i)?.label??r.message,recommended:r.editable.recommended,required:r.required,validate:r.editable.validate}))).trim()};let a=String(e[r.editable.key]),o=r.editable.validate?.(a);if(o)throw new InvalidAnswerError(text({key:r.editable.key,message:r.editable.label,required:r.required}),o);let s=a.trim();return announce(t,r.editable.key,s,`answer`),{value:i,text:s}},async askMany(r){if(r.key in e){let n=coerceManyAnswer(r,e[r.key]);return announce(t,r.key,n,`answer`)}return n.askMany(r)}})}function withRequired(e){return t=>({async ask(n){return!n.required&&e.includes(n.key)?t.ask({...n,required:!0}):t.ask(n)},async askEditable(n){return t.askEditable(!n.required&&e.includes(n.key)?{...n,required:!0}:n)},async askMany(n){return!n.required&&e.includes(n.key)?t.askMany({...n,required:!0}):t.askMany(n)}})}function withPolicy(e,t){return n=>({async ask(r){if(e===`assume`){if(r.detected!==void 0)return r.kind===`select`&&requiredOptionId(r,r.detected,`detected value`),announce(t,r.key,r.detected,`detected`);if(r.recommended!==void 0)return r.kind===`select`&&requiredOptionId(r,r.recommended,`recommendation`),announce(t,r.key,r.recommended,`assumed`);if(!r.required)throw announce(t,r.key,void 0,`skipped`),new SkippedSignal(r.key);return n.ask(r)}return r.detected!==void 0&&await n.ask(confirm({key:r.key,message:`Use the detected value for "${r.key}"?`,internal:!0}))?announce(t,r.key,r.detected,`detected`):n.ask(r)},async askEditable(r){if(e!==`assume`||r.recommended===void 0)return n.askEditable(r);requiredOptionId(r,r.editable.value,`configured editable value`),requiredOptionId(r,r.recommended,`recommendation`);let i=r.recommended===r.editable.value?{value:r.recommended,text:r.editable.recommended}:{value:r.recommended};return announce(t,r.key,i.value,`assumed`),i.text!==void 0&&announce(t,r.editable.key,i.text,`assumed`),i},async askMany(r){if(e===`assume`){if(r.detected!==void 0){for(let e of r.detected)requiredOptionId(r,e,`detected value`);return announce(t,r.key,[...r.detected],`detected`)}if(r.recommended!==void 0){for(let e of r.recommended)requiredOptionId(r,e,`recommendation`);return announce(t,r.key,[...r.recommended],`assumed`)}if(!r.required)throw announce(t,r.key,void 0,`skipped`),new SkippedSignal(r.key);return n.askMany(r)}return r.detected!==void 0&&await n.ask(confirm({key:r.key,message:`Use the detected value for "${r.key}"?`,internal:!0}))?announce(t,r.key,[...r.detected],`detected`):n.askMany(r)}})}export{InteractionRequired,InvalidAnswerError,SkippedSignal,confirm,headlessAsker,interactiveAsker,select,text,withAnswers,withPolicy,withRequired};