@neuvybe/jarvis 0.13.1 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -218,7 +218,7 @@ ${c}${s}
|
|
|
218
218
|
${a}`:""}`}default:{let s=e?`${v("cyan",D)} `:"",a=e?v("cyan",tt):"";return`${n}${s}${r}
|
|
219
219
|
${a}
|
|
220
220
|
`}}}}).prompt();Ve();var g1={brain:["text"],voice:["stt","tts"],memory:["embedding"]},_x=["brain","voice","memory"],Yp="brain",v1=["text","stt","tts","embedding"];function _1(t){return t==="text"}function $x(t){let e=new Set;for(let n of t)for(let i of g1[n])e.add(i);return v1.filter(n=>e.has(n))}function yx(t){let e=["brain"],n=t.routes;return(n?.stt||n?.tts)&&e.push("voice"),n?.embedding&&e.push("memory"),e}function bx(t,e){if(e==="valid")return[];let n=_1(t);return e==="invalid"?n?["retry","change"]:["retry","change","skip"]:n?["save","retry","change"]:["save","retry","change","skip"]}function xx(t){let e={},n={};for(let r of t.routes){e[r.capability]={provider:r.provider,model:r.model};let o=t.keys.get(r.provider);o!==void 0&&o.length>0&&(n[r.provider]=o)}let i={version:t.existing.version,defaults:{logLevel:t.logLevel,modality:t.modality},upgrade:{auto:t.existing.upgrade.auto}};return Object.keys(e).length>0&&(i.routes=e),Object.keys(n).length>0&&(i.secrets=n),i}function Sx(t,e){return!!t.isTTY&&!!e.isTTY}var mi=class extends Error{},wx={save:{label:"save anyway",hint:"keep this key; verify later with jarvis doctor"},retry:{label:"re-enter the key"},change:{label:"choose a different provider"},skip:{label:"skip this capability",hint:"optional"}},ua=class{config;probe;io;stdin;stdout;constructor(e){this.config=e.config,this.probe=e.probe,this.io=e.io??ae,this.stdin=e.stdin??process.stdin,this.stdout=e.stdout??process.stdout}async run(){if(!Sx(this.stdin,this.stdout))return this.printHeadlessGuidance(),1;try{return await this.wizard()}catch(e){if(e instanceof mi)return dx("no changes written."),130;throw e}}loadExisting(){try{return{config:this.config.resolved(),corrupt:!1}}catch{return{config:Ft.parse({}),corrupt:!0}}}async wizard(){let{config:e,corrupt:n}=this.loadExisting(),i=!n&&!!(e.routes??e.secrets);fx(z.bold("jarvis init")),n&&Pe.warn("the existing config.json is invalid; init will replace it with a fresh config."),Xp(`Writes ${z.accent(je.resolve("config.json"))} (owner-only).`+(i?`
|
|
221
|
-
A config already exists - values are prefilled; edit or keep them.`:""),"setup");let r=await this.pickCapabilities(e),o=new Map,s=[];for(let d of $x(r)){let f=await this.configureCapability(d,e,o);f&&s.push(f)}let a=this.ensure(await pi({message:"Default modality",options:[{value:"voice",label:"voice",hint:"speak and listen when available"},{value:"text",label:"text",hint:"text-only"}],initialValue:e.defaults.modality})),c=this.ensure(await pi({message:"Log level",options:["debug","info","warn","error"].map(d=>({value:d,label:d})),initialValue:e.defaults.logLevel}));if(this.review(s,o,a,c),!this.ensure(await ca({message:"Write this configuration?"})))throw new mi;return this.config.write(xx({existing:e,routes:s,keys:o,modality:a,logLevel:c})),Pe.success(`wrote ${je.resolve("config.json")}`),await ca({message:"Start Jarvis now?",initialValue:!1})===!0&&Pe.info("starting is not wired yet in v0.1 - run `jarvis` to begin a session."),px(z.ok("done.")),0}async pickCapabilities(e){let n=this.ensure(await mx({message:"Which capabilities do you want to set up?",options:[{value:"brain",label:"Brain (text)",hint:"required - the reasoning model"},{value:"voice",label:"Voice (speech in + out)",hint:"optional"},{value:"memory",label:"Memory (embeddings)",hint:"optional"}],initialValues:yx(e),required:!1}));n.includes(Yp)||Pe.warn("Brain (text) is required; keeping it selected.");let i=new Set(n);return i.add(Yp),_x.filter(r=>i.has(r))}async configureCapability(e,n,i){let r=this.probe.providersFor(e);if(r.length===0)return Pe.warn(`no providers available for ${e}; skipping.`),null;for(;;){let o=await this.pickProvider(e,r,n),s=await this.pickModel(e,o,n);if(i.has(o))return{capability:e,provider:o,model:s};let a=await this.resolveKey(e,o,s,n,i);if(a!=="change-provider")return a}}async resolveKey(e,n,i,r,o){let s=await this.firstKey(n,r);for(;;){let a=await this.validateKey(n,s);if(a==="valid")return o.set(n,s),{capability:e,provider:n,model:i};let c=await this.pickAfterFailure(e,a);if(c==="save")return o.set(n,s),{capability:e,provider:n,model:i};if(c==="skip")return Pe.info(`skipped ${e} (optional).`),null;if(c==="change")return"change-provider";s=await this.promptPassword(n)}}async pickProvider(e,n,i){let r=i.routes?.[e]?.provider;return this.ensure(await pi({message:`${e} provider`,options:n.map(o=>({value:o,label:o})),initialValue:r&&n.includes(r)?r:void 0}))}async pickModel(e,n,i){let r=i.routes?.[e],o=r?.provider===n?r.model:this.probe.defaultModel(n,e)??"";return this.ensure(await vx({message:`${e} model`,initialValue:o,validate:s=>s?.trim()?void 0:"a model is required"}))}async firstKey(e,n){let i=n.secrets?.[e];return i&&this.ensure(await ca({message:`Reuse the saved key for ${e}?`,initialValue:!0}))?i:this.promptPassword(e)}async promptPassword(e){return this.ensure(await hx({message:`${e} API key`,validate:
|
|
221
|
+
A config already exists - values are prefilled; edit or keep them.`:""),"setup");let r=await this.pickCapabilities(e),o=new Map,s=[];for(let d of $x(r)){let f=await this.configureCapability(d,e,o);f&&s.push(f)}let a=this.ensure(await pi({message:"Default modality",options:[{value:"voice",label:"voice",hint:"speak and listen when available"},{value:"text",label:"text",hint:"text-only"}],initialValue:e.defaults.modality})),c=this.ensure(await pi({message:"Log level",options:["debug","info","warn","error"].map(d=>({value:d,label:d})),initialValue:e.defaults.logLevel}));if(this.review(s,o,a,c),!this.ensure(await ca({message:"Write this configuration?"})))throw new mi;return this.config.write(xx({existing:e,routes:s,keys:o,modality:a,logLevel:c})),Pe.success(`wrote ${je.resolve("config.json")}`),await ca({message:"Start Jarvis now?",initialValue:!1})===!0&&Pe.info("starting is not wired yet in v0.1 - run `jarvis` to begin a session."),px(z.ok("done.")),0}async pickCapabilities(e){let n=this.ensure(await mx({message:"Which capabilities do you want to set up?",options:[{value:"brain",label:"Brain (text)",hint:"required - the reasoning model"},{value:"voice",label:"Voice (speech in + out)",hint:"optional"},{value:"memory",label:"Memory (embeddings)",hint:"optional"}],initialValues:yx(e),required:!1}));n.includes(Yp)||Pe.warn("Brain (text) is required; keeping it selected.");let i=new Set(n);return i.add(Yp),_x.filter(r=>i.has(r))}async configureCapability(e,n,i){let r=this.probe.providersFor(e);if(r.length===0)return Pe.warn(`no providers available for ${e}; skipping.`),null;for(;;){let o=await this.pickProvider(e,r,n),s=await this.pickModel(e,o,n);if(i.has(o))return{capability:e,provider:o,model:s};let a=await this.resolveKey(e,o,s,n,i);if(a!=="change-provider")return a}}async resolveKey(e,n,i,r,o){let s=await this.firstKey(n,r);for(;;){let a=await this.validateKey(n,s);if(a==="valid")return o.set(n,s),{capability:e,provider:n,model:i};let c=await this.pickAfterFailure(e,a);if(c==="save")return o.set(n,s),{capability:e,provider:n,model:i};if(c==="skip")return Pe.info(`skipped ${e} (optional).`),null;if(c==="change")return"change-provider";s=await this.promptPassword(n)}}async pickProvider(e,n,i){let r=i.routes?.[e]?.provider;return this.ensure(await pi({message:`${e} provider`,options:n.map(o=>({value:o,label:o})),initialValue:r&&n.includes(r)?r:void 0}))}async pickModel(e,n,i){let r=i.routes?.[e],o=r?.provider===n?r.model:this.probe.defaultModel(n,e)??"";return this.ensure(await vx({message:`${e} model`,initialValue:o,validate:s=>s?.trim()?void 0:"a model is required"}))}async firstKey(e,n){let i=n.secrets?.[e];return i&&this.ensure(await ca({message:`Reuse the saved key for ${e}?`,initialValue:!0}))?i.trim():this.promptPassword(e)}async promptPassword(e){return this.ensure(await hx({message:`${e} API key`,validate:i=>i?.trim()?void 0:"a key is required"})).trim()}async validateKey(e,n){let i=gx();i.start(`validating ${e} key...`);let r=await this.probe.probe(e,n),o=r.detail?` (${r.detail})`:"";return r.ok?i.stop(z.ok(`${e} key valid`)):r.status==="invalid"?i.stop(z.fail(`${e} key rejected${o}`)):i.stop(z.warn(`could not validate ${e}${o}`)),r.status}async pickAfterFailure(e,n){let i=bx(e,n);return this.ensure(await pi({message:`${e}: how do you want to proceed?`,options:i.map(r=>({value:r,label:wx[r].label,hint:wx[r].hint}))}))}review(e,n,i,r){let o=[];if(e.length===0)o.push("routes: none");else for(let s of e){let a=n.has(s.provider)?z.ok("key set"):z.warn("no key");o.push(`${s.capability}: ${s.provider}/${s.model} (${a})`)}o.push(`defaults: modality=${i}, logLevel=${r}`),Xp(o.join(`
|
|
222
222
|
`),"review (secrets redacted)")}printHeadlessGuidance(){this.io.err(["jarvis init needs an interactive terminal.","","On a headless or non-interactive host:"," - run `jarvis init` over an interactive SSH session, or"," - set secrets and defaults non-interactively, e.g.:"," jarvis config set secrets.anthropic <key>"," jarvis config set defaults.modality text"," - relocate the home with the JARVIS_HOME env var if needed.","","Note: configuring a provider route (provider + model together) is interactive","in v0.1 - use `jarvis init` for that. Verify any setup with: jarvis doctor"].join(`
|
|
223
223
|
`)+`
|
|
224
224
|
`)}ensure(e){if(ia(e))throw new mi;return e}};import{randomUUID as $1}from"node:crypto";var Qp=class{constructor(e,n,i=ae){this.session=e;this.reader=n;this.io=i}session;reader;io;prompt="you ";labelShown=!1;async run(){try{for await(let e of this.session.events())if(!await this.handle(e))break}finally{this.reader.close(),await this.session.close()}}async handle(e){switch(e.type){case"session.ready":return this.greet(),this.promptAndSend();case"turn.accepted":return this.labelShown=!1,!0;case"state":return e.state==="responding"&&this.showLabel(),!0;case"response.delta":return this.showLabel(),this.io.out(e.text),!0;case"response.done":return this.labelShown&&this.io.out(`
|