@kianwoon/modelweaver 0.3.15 → 0.3.17
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/{chunk-SCUI4R77.js → chunk-BXLSPBXT.js} +2 -2
- package/dist/{chunk-ZF23JS2D.js → chunk-F72SPY6C.js} +2 -2
- package/dist/{chunk-ZF23JS2D.js.map → chunk-F72SPY6C.js.map} +1 -1
- package/dist/config-CE7DQIUM.js +3 -0
- package/dist/{daemon-27GXX25D.js → daemon-WP5LZ7DG.js} +2 -2
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/dist/init-V5J734NZ.js +64 -0
- package/dist/init-V5J734NZ.js.map +1 -0
- package/package.json +1 -1
- package/dist/config-P34YQCFG.js +0 -3
- package/dist/init-VLTKSOZN.js +0 -63
- package/dist/init-VLTKSOZN.js.map +0 -1
- /package/dist/{chunk-SCUI4R77.js.map → chunk-BXLSPBXT.js.map} +0 -0
- /package/dist/{config-P34YQCFG.js.map → config-CE7DQIUM.js.map} +0 -0
- /package/dist/{daemon-27GXX25D.js.map → daemon-WP5LZ7DG.js.map} +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import p from"prompts";var G=[{id:"anthropic",name:"Anthropic",baseUrl:"https://api.anthropic.com",envKey:"ANTHROPIC_API_KEY",authType:"anthropic",testPath:"/v1/messages",models:{sonnet:"claude-sonnet-4-20250514",opus:"claude-opus-4-20250514",haiku:"claude-haiku-4-5-20251001"}},{id:"openrouter",name:"OpenRouter",baseUrl:"https://openrouter.ai/api",envKey:"OPENROUTER_API_KEY",authType:"bearer",testPath:"/v1/chat/completions",models:{sonnet:"anthropic/claude-sonnet-4",opus:"anthropic/claude-opus-4",haiku:"anthropic/claude-haiku-4"}},{id:"together",name:"Together AI",baseUrl:"https://api.together.xyz",envKey:"TOGETHER_API_KEY",authType:"bearer",testPath:"/v1/chat/completions",models:{sonnet:"meta-llama/Llama-3.3-70B-Instruct-Turbo",opus:"meta-llama/Llama-3.3-70B-Instruct-Turbo",haiku:"meta-llama/Llama-3.3-70B-Instruct-Turbo"}},{id:"glm",name:"GLM (Z.ai)",baseUrl:"https://api.z.ai/api/anthropic",envKey:"GLM_API_KEY",authType:"anthropic",testPath:"/v1/messages",models:{sonnet:"claude-sonnet-4-20250514",opus:"claude-opus-4-20250514",haiku:"claude-haiku-4-5-20251001"}},{id:"minimax",name:"Minimax",baseUrl:"https://api.minimax.io/anthropic",envKey:"MINIMAX_API_KEY",authType:"anthropic",testPath:"/v1/messages",models:{sonnet:"MiniMax-M2.7",opus:"MiniMax-M2.7",haiku:"MiniMax-M2.7"}},{id:"fireworks",name:"Fireworks",baseUrl:"https://api.fireworks.ai/inference/v1",envKey:"FIREWORKS_API_KEY",authType:"bearer",testPath:"/chat/completions",models:{sonnet:"accounts/fireworks/models/claude-sonnet-4",opus:"accounts/fireworks/models/claude-opus-4",haiku:"accounts/fireworks/models/claude-haiku-4"}}];function H(){return G}function B(o){return G.find(t=>t.id===o)}import{stringify as be}from"yaml";import{writeFileSync as oe,existsSync as ne,readFileSync as ye,mkdirSync as ie}from"fs";import{join as O}from"path";import{readFileSync as ce,writeFileSync as de,existsSync as q,copyFileSync as pe,mkdirSync as ge,renameSync as me,unlinkSync as fe}from"fs";import{join as L,dirname as he}from"path";import{homedir as ve}from"os";var Y=L(ve(),".claude"),C=L(Y,"settings.json"),$e=L(Y,"settings.json.bak");function z(){return C}function V(){if(!q(C))return{};let o=ce(C,"utf-8");try{return JSON.parse(o)}catch(t){throw console.error("[settings] Failed to parse settings.json:",t),t}}function J(){return q(C)?(console.log("[settings] Backing up existing settings to .bak"),pe(C,$e),!0):!1}function Q(o,t){let n={...o};n.env={...o.env||{}};let e={ANTHROPIC_BASE_URL:t.baseUrl};for(let[i,s]of Object.entries(e))n.env[i]=s;if(t.tierModels){let i={sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL"};for(let[s,r]of Object.entries(i)){let a=t.tierModels[s];a&&(n.env[r]=a)}}return t.defaultModel&&(n.model=t.defaultModel),t.availableModels&&t.availableModels.length>0&&(n.availableModels=t.availableModels),n}function X(o){ge(he(C),{recursive:!0});let t=C+".tmp";try{de(t,JSON.stringify(o,null,2)+`
|
|
3
|
+
`,"utf-8"),me(t,C)}catch(n){console.error("[settings] Failed to write settings.json:",n);try{fe(t)}catch{}throw n}}import ke from"net";var I=class extends Error{constructor(){super("__back__"),this.name="GoBackError"}},g={onCancel:()=>{throw new I}},re="\x1B[32m",A="\x1B[31m",m="\x1B[36m",x="\x1B[1m",d="\x1B[0m";function M(o){console.log(` ${re}\u2713${d} ${o}`)}function we(o){console.log(` ${A}\u2717${d} ${o}`)}function j(){console.log(`
|
|
4
|
+
${"\u2500".repeat(56)}
|
|
5
|
+
`)}async function Z(o,t,n){let e=new AbortController,i=setTimeout(()=>e.abort(),5e3),s=n.authType==="anthropic"?{"x-api-key":t,"anthropic-version":"2023-06-01","anthropic-beta":"interleaved-thinking-2025-05-14","content-type":"application/json"}:{Authorization:`Bearer ${t}`,"content-type":"application/json"};try{let r=await fetch(`${o}${n.testPath}`,{method:"POST",headers:s,body:JSON.stringify({model:n.models.sonnet,max_tokens:1,messages:[{role:"user",content:"hi"}]}),signal:e.signal});if(r.status===401||r.status===403)return{ok:!1,error:"Invalid API key"};if(r.status===200||r.status===400||r.status===429)return{ok:!0};try{if((await r.json()).error?.message?.includes("insufficient balance"))return{ok:!0}}catch{}return{ok:!1,error:`Unexpected status ${r.status}`}}catch(r){return r.name==="AbortError"?{ok:!1,error:"Request timed out"}:{ok:!1,error:"Network error \u2014 endpoint unreachable"}}finally{clearTimeout(i)}}function ee(o){let t=process.env[o.envKey];return t&&t.trim()?{found:!0,key:t.trim(),source:"environment"}:{found:!1,key:"",source:""}}function E(o,t){return t?3:o+1+1+1}function se(o,t,n){let e=[];e.push(`${m}\u250C${"\u2500".repeat(56)}\u2510${d}`),e.push(`${m}\u2502${d}${x} ModelWeaver Configuration Summary${"".padEnd(23)}${m}\u2502${d}`),e.push(`${m}\u251C${"\u2500".repeat(56)}\u2524${d}`),e.push(`${m}\u2502${d} ${x}Server:${d} ${n.host}:${n.port}${"".padEnd(48-`${n.host}:${n.port}`.length)}${m}\u2502${d}`),e.push(`${m}\u251C${"\u2500".repeat(56)}\u2524${d}`);for(let s of t){let r=o.find(l=>l.id===s.provider),a=r?r.name:s.provider,u=`${s.alias.padEnd(16)} ${a} \u2192 ${s.model}`;e.push(`${m}\u2502${d} ${u}${"".padEnd(Math.max(0,56-u.length-2))}${m}\u2502${d}`);for(let l of s.fallbacks){let c=o.find(v=>v.id===l.provider),b=` fallback: ${c?c.name:l.provider} \u2192 ${l.model}`;e.push(`${m}\u2502${d} ${b}${"".padEnd(Math.max(0,56-b.length-2))}${m}\u2502${d}`)}}return e.push(`${m}\u2514${"\u2500".repeat(56)}\u2518${d}`),e.join(`
|
|
6
|
+
`)}function Pe(o){let t=[],e=[...o.entries()];t.push(`${m}${"\u250C"+"\u2500".repeat(56)+"\u2510"}${d}`),t.push(`${m}\u2502${d}${x} Currently Configured Providers${"".padEnd(26)}${m}\u2502${d}`),t.push(`${m}\u251C${"\u2500".repeat(56)}\u2524${d}`);for(let[i,s]of e){t.push(`${m}\u2502${d} ${x}${i}${d}${"".padEnd(Math.max(0,20-i.length))} ${s.baseUrl}${"".padEnd(Math.max(0,34-s.baseUrl.length))}${m}\u2502${d}`);let r=s.envKey||"(hardcoded key)",a=Math.max(0,34-r.length-s.authType.length);t.push(`${m}\u2502${d} env: ${r} auth: ${s.authType}${"".padEnd(a)}${m}\u2502${d}`)}return t.push(`${m}${"\u2514"+"\u2500".repeat(56)+"\u2518"}${d}`),t.join(`
|
|
7
|
+
`)}function Se(o,t){let n=new Set(o.map(i=>i.id)),e=[...o];for(let[i,s]of t.entries())if(!n.has(i)){let r=B(i);e.push({id:i,name:r?.name??i,baseUrl:s.baseUrl,envKey:s.envKey,apiKey:"",authType:s.authType,models:r?.models??{sonnet:"",opus:"",haiku:""}})}return e}async function W(o){let t=H();if(o?.excludeIds&&(t=t.filter(e=>!o.excludeIds.has(e.id))),t.length===0)return[];if(o?.singleSelect){let{providerId:e}=await p({type:"select",name:"providerId",message:"Select a provider:",choices:[{title:"\u2B05 Go back",value:"__back__"},...t.map(i=>({title:i.name,value:i.id,description:i.baseUrl}))]},g);return[e]}let{providerIds:n}=await p({type:"multiselect",name:"providerIds",message:"Select providers to configure:",choices:t.map(e=>({title:e.name,value:e.id,description:e.baseUrl})),min:1},g);return n}async function T(o,t,n){let e=B(o);if(!e)return console.error(` Error: Unknown provider "${o}". Skipping.`),null;let i=n?.envKey??e.envKey,s=n?.baseUrl??e.baseUrl,r=n?.authType??e.authType,a=ee(e);if(n?.envKey&&n.envKey!==e.envKey){let c=ee({...e,envKey:n.envKey});c.found&&(a=c)}if(a.found){process.stdout.write(` Testing API key for ${e.name} (from ${a.source})...`);let c=await Z(s,a.key,e);if(process.stdout.write("\r"+" ".repeat(60)+"\r"),c.ok){M(`${e.name}: existing ${i} is valid (${a.source})`);let{useExisting:f}=await p({type:"confirm",name:"useExisting",message:`Use this key for ${e.name}?`,initial:!0},g);if(f)return{id:o,name:e.name,baseUrl:s,envKey:i,apiKey:a.key,authType:r,models:e.models};console.log(` Enter a new key for ${e.name}:`)}console.log(` ${A}\u26A0${d} Existing ${i} (${a.source}) is invalid, please provide a new key.`)}let u=t?`[Step ${t.current} of ${t.total}] `:"",l=3;for(let c=0;c<l;c++){let{baseUrl:f}=await p({type:"text",name:"baseUrl",message:`${u}[${e.name}] Base URL:`,initial:s},g);try{new URL(f)}catch{if(console.log(" Invalid URL format. Please try again."),c<l-1)continue;let{retry:P}=await p({type:"confirm",name:"retry",message:`Retry with a valid URL? (${c+1}/${l-1} retries used)`,initial:!0},g);if(!P)break;continue}let{apiKey:b}=await p({type:"password",name:"apiKey",message:`${u}[${e.name}] API key:`},g);process.stdout.write(` Testing API key for ${e.name}...`);let v=await Z(f,b,e);if(process.stdout.write("\r"+" ".repeat(60)+"\r"),v.ok)return M(`${e.name} API key accepted`),{id:o,name:e.name,baseUrl:f,envKey:i,apiKey:b,authType:r,models:e.models};if(we(`${e.name}: ${v.error}`),c<l-1){let{retry:P}=await p({type:"confirm",name:"retry",message:`Retry? (${c+1}/${l-1} retries used)`,initial:!0},g);if(!P)break}}return console.log(` ${A}Warning:${d} ${e.name} will be skipped \u2014 max retries (${l}) exceeded.`),null}async function U(o,t,n,e=[]){let i=[...e];for(;;)try{let{addFallback:s}=await p({type:"confirm",name:"addFallback",message:i.length===0?`Add a fallback provider for ${o}?`:`Add another fallback provider for ${o}?`,initial:!1},g);if(!s)break;let r=n.filter(f=>f.id!==t);if(r.length===0){console.log(` ${A}No other providers available for fallback.${d}`);break}let a=r.map(f=>({title:f.name,value:f.id})),{providerId:u}=await p({type:"select",name:"providerId",message:`Select fallback provider for ${o}:`,choices:[{title:"\u2B05 Go back",value:"__back__"},...a]},g);if(u==="__back__")continue;let l=n.find(f=>f.id===u),{modelName:c}=await p({type:"text",name:"modelName",message:`[${l.name}] Fallback model name:`,initial:""},g);i.push({provider:l.id,model:c.trim()}),M(`Added fallback: ${l.id} \u2192 ${c.trim()}`)}catch(s){if(s instanceof I)break;throw s}return i}function _e(o){let t=[],e=[...o.entries()];if(t.push(`${m}${"\u250C"+"\u2500".repeat(56)+"\u2510"}${d}`),t.push(`${m}\u2502${d}${x} Currently Configured Models${"".padEnd(28)}${m}\u2502${d}`),t.push(`${m}\u251C${"\u2500".repeat(56)}\u2524${d}`),e.length===0)t.push(`${m}\u2502${d} (none)${"".padEnd(49)}${m}\u2502${d}`);else for(let[i,s]of e){let r=s[0],a=`${i.padEnd(20)} ${r.provider} \u2192 ${r.model}`;t.push(`${m}\u2502${d} ${a}${"".padEnd(Math.max(0,56-a.length-2))}${m}\u2502${d}`);for(let u=1;u<s.length;u++){let l=` fallback: ${s[u].provider} \u2192 ${s[u].model}`;t.push(`${m}\u2502${d} ${l}${"".padEnd(Math.max(0,56-l.length-2))}${m}\u2502${d}`)}}return t.push(`${m}${"\u2514"+"\u2500".repeat(56)+"\u2518"}${d}`),t.join(`
|
|
8
|
+
`)}async function Me(o,t){let n=[],e=t&&t.size>0;if(e)for(let[i,s]of t.entries()){let r=s[0];n.push({alias:i,provider:r.provider,model:r.model,fallbacks:s.slice(1)})}if(console.log(),console.log(" Configure models \u2014 each model gets an alias that appears in Claude Code's /model picker."),console.log(" The proxy will route requests matching the alias to the chosen provider."),console.log(),e)for(;;){console.log(_e(t)),console.log();let{action:i}=await p({type:"select",name:"action",message:"What would you like to do?",choices:[{title:"Add new model",value:"add",description:"Add a model alias routed to a provider"},...n.length>0?[{title:"Edit existing model",value:"edit",description:"Change alias, provider, or model name"}]:[],...n.length>0?[{title:"Delete model",value:"delete",description:"Remove a model alias"}]:[],{title:"Done",value:"done",description:"Continue to server configuration"}]},g);if(i==="done")break;if(i==="add"){let s=o.map(f=>({title:f.name,value:f.id})),{providerId:r}=await p({type:"select",name:"providerId",message:"Select provider:",choices:[{title:"\u2B05 Go back",value:"__back__"},...s]},g);if(r==="__back__")continue;let a=o.find(f=>f.id===r),{alias:u}=await p({type:"text",name:"alias",message:`[${a.name}] Model alias (name in /model picker):`,initial:""},g),{modelName:l}=await p({type:"text",name:"modelName",message:`[${a.name}] Actual model name (sent to provider API):`,initial:u},g),c={alias:u.trim(),provider:a.id,model:l.trim(),fallbacks:[]};c.fallbacks=await U(c.alias,c.provider,o,c.fallbacks),n.push(c),t.set(c.alias,[{provider:c.provider,model:c.model},...c.fallbacks]),M(`Added model: ${c.alias}`)}if(i==="edit"){let s=n.map(h=>({title:h.alias,value:h.alias,description:`${h.provider} \u2192 ${h.model}`})),{editAlias:r}=await p({type:"select",name:"editAlias",message:"Select model to edit:",choices:[{title:"\u2B05 Go back",value:"__back__"},...s]},g);if(r==="__back__")continue;let a=n.findIndex(h=>h.alias===r);if(a===-1)continue;let u=n[a],l=[...u.fallbacks],{alias:c}=await p({type:"text",name:"alias",message:"Model alias:",initial:u.alias},g),f=o.map(h=>({title:h.name,value:h.id})),{providerId:b}=await p({type:"select",name:"providerId",message:"Select provider:",choices:[{title:"\u2B05 Go back",value:"__back__"},...f]},g);if(b==="__back__")continue;let v=o.find(h=>h.id===b),{modelName:P}=await p({type:"text",name:"modelName",message:"Actual model name:",initial:u.model},g),$=c.trim(),y=P.trim();for(;;){if(l.length===0)console.log(` ${m}No fallbacks configured.${d}`);else{console.log(` ${m}Current fallbacks:${d}`);for(let w=0;w<l.length;w++){let _=l[w],S=o.find(K=>K.id===_.provider),R=S?S.name:_.provider;console.log(` ${w+1}. ${R} \u2192 ${_.model}`)}}console.log();let h=[{title:"Add fallback",value:"add_fb"},...l.length>0?[{title:"Remove fallback",value:"remove_fb"}]:[],{title:"Done editing fallbacks",value:"done_fb"}],{fallbackAction:k}=await p({type:"select",name:"fallbackAction",message:"Manage fallbacks:",choices:h},g);if(k==="done_fb")break;if(k==="add_fb"&&(l=await U($,v.id,o,l)),k==="remove_fb"){let w=l.map((S,R)=>{let K=o.find(le=>le.id===S.provider);return{title:`${K?K.name:S.provider} \u2192 ${S.model}`,value:R}}),{removeIdx:_}=await p({type:"select",name:"removeIdx",message:"Select fallback to remove:",choices:[{title:"\u2B05 Go back",value:"__back__"},...w]},g);if(_!=="__back__"){let S=l.splice(_,1);M(`Removed fallback: ${S[0].provider} \u2192 ${S[0].model}`)}}}$!==u.alias&&t.delete(u.alias),n[a]={alias:$,provider:v.id,model:y,fallbacks:l},t.set($,[{provider:v.id,model:y},...l]),M(`Updated model: ${$} (${v.id} \u2192 ${y})`)}if(i==="delete"){let s=n.map(u=>({title:u.alias,value:u.alias,description:`${u.provider} \u2192 ${u.model}`})),{deleteAlias:r}=await p({type:"select",name:"deleteAlias",message:"Select model to delete:",choices:[{title:"\u2B05 Go back",value:"__back__"},...s]},g);if(r==="__back__")continue;let a=n.findIndex(u=>u.alias===r);a!==-1&&(n.splice(a,1),t.delete(r),M(`Deleted model: ${r}`))}j()}else{for(let i of o){let{addModel:s}=await p({type:"confirm",name:"addModel",message:`Add a model for ${i.name}?`,initial:!0},g);if(s){let r=i.models?.sonnet||i.models?.opus||i.models?.haiku||"",{alias:a}=await p({type:"text",name:"alias",message:`[${i.name}] Model alias (name in /model picker):`,initial:r||i.id},g),{modelName:u}=await p({type:"text",name:"modelName",message:`[${i.name}] Actual model name (sent to provider API):`,initial:r},g),l={alias:a.trim(),provider:i.id,model:u.trim(),fallbacks:[]};l.fallbacks=await U(l.alias,l.provider,o,l.fallbacks),n.push(l)}}for(;;){console.log();let{addMore:i}=await p({type:"confirm",name:"addMore",message:n.length===0?"Add a model?":"Add another model?",initial:n.length===0},g);if(!i)break;let s=o.map(f=>({title:f.name,value:f.id})),{providerId:r}=await p({type:"select",name:"providerId",message:"Select provider:",choices:s},g),a=o.find(f=>f.id===r),{alias:u}=await p({type:"text",name:"alias",message:`[${a.name}] Model alias:`,initial:""},g),{modelName:l}=await p({type:"text",name:"modelName",message:`[${a.name}] Actual model name:`,initial:u},g),c={alias:u.trim(),provider:a.id,model:l.trim(),fallbacks:[]};c.fallbacks=await U(c.alias,c.provider,o,c.fallbacks),n.push(c)}}return n.length===0?console.log(" No models configured."):M(`${n.length} model(s) configured`),n}function xe(o,t){return new Promise(n=>{let e=ke.createServer();e.once("error",()=>{e.close(),n(!0)}),e.once("listening",()=>{e.close(),n(!1)}),e.listen(o,t)})}async function Ce(o){if(o?.useDefaults)return{port:3456,host:"localhost"};let t=o?.stepInfo?`[Step ${o.stepInfo.current} of ${o.stepInfo.total}] `:"",{port:n}=await p({type:"number",name:"port",message:`${t}Server port:`,initial:3456},g),{host:e}=await p({type:"text",name:"host",message:`${t}Server host:`,initial:"localhost"},g);if(await xe(n,e)){console.log(` ${A}\u26A0 Warning:${d} Port ${n} is already in use on ${e}.`);let{proceed:i}=await p({type:"confirm",name:"proceed",message:"Use this port anyway?",initial:!1},g);i||(console.log(" Please choose a different port and try again."),process.exit(1))}return{port:n,host:e}}async function Ee(o){if(o.length===0)return null;try{console.log();let{configure:t}=await p({type:"confirm",name:"configure",message:"Configure Claude Code to use ModelWeaver automatically?",initial:!0},g);if(!t)return null;let{defaultModel:n}=await p({type:"select",name:"defaultModel",message:"Select default model for Claude Code:",choices:o.map(i=>({title:i.alias,description:`${i.provider} \u2192 ${i.model}`,value:i.alias}))},g),e=o.map(i=>i.alias);return{defaultModel:n,availableModels:e}}catch(t){if(t instanceof I)return null;throw t}}function ae(o,t,n,e,i){let s={};for(let a of t)s[a.alias]=[{provider:a.provider,model:a.model},...a.fallbacks];if(i)for(let[a,u]of i.entries())s[a]||(s[a]=u.map(l=>({provider:l.provider,model:l.model})));let r={server:n,providers:{},modelRouting:s};for(let a of o){let u={baseUrl:a.baseUrl,apiKey:`\${${a.envKey}}`,timeout:3e4};a.authType==="bearer"&&(u.authType="bearer"),r.providers[a.id]=u}if(e){let a=new Set(o.map(u=>u.id));for(let[u,l]of e.entries())if(!a.has(u)){let c={baseUrl:l.baseUrl,apiKey:l.envKey?`\${${l.envKey}}`:"",timeout:l.timeout};l.authType==="bearer"&&(c.authType="bearer"),r.providers[u]=c}}return be(r)}function Ae(o){let t=O(process.env.HOME||process.env.USERPROFILE||"",".modelweaver"),n=O(t,".env");ie(t,{recursive:!0});let e="";ne(n)&&(e=ye(n,"utf-8")),e&&!e.endsWith(`
|
|
9
|
+
`)&&(e+=`
|
|
10
|
+
`);let i=[];for(let s of o){let r=s.envKey.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^${r}=.*$`,"m"),u=s.apiKey.includes('"')?s.apiKey.includes("'")?`'${s.apiKey.replace(/'/g,"'\\''")}'`:`'${s.apiKey}'`:`"${s.apiKey}"`;a.test(e)?e=e.replace(a,`${s.envKey}=${u}`):i.push(`${s.envKey}=${u}`)}oe(n,e+i.join(`
|
|
11
|
+
`)+(i.length>0?`
|
|
12
|
+
`:""),{mode:384})}async function Ie(){process.stdin.isTTY||(console.error("Error: modelweaver init --quick requires an interactive terminal."),process.exit(1));let{peekConfig:o}=await import("./config-CE7DQIUM.js"),t=o(),n=t?.providers??new Map,e=t?.modelRouting??new Map,i=[],s=[],r,a,u=E(1,!0),l=!1;for(;;)try{j(),console.log(`
|
|
13
|
+
${x}${m}\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u2500
|
|
14
|
+
\u2502 Welcome to ModelWeaver! \u2501 Quick Setup \u2502
|
|
15
|
+
\u2502 \u2502
|
|
16
|
+
\u2501 ~${String(u).padEnd(3)} quick steps to get started \u2501
|
|
17
|
+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518${d}
|
|
18
|
+
`);let c=await W({singleSelect:!0});if(l=!0,c.length===1&&c[0]==="__back__")continue;i=[];for(let v of c){let P=await T(v,{current:1,total:u});P&&i.push(P)}i.length===0&&(console.log(`
|
|
19
|
+
${A}No providers configured. Exiting.${d}
|
|
20
|
+
`),process.exit(1));let f=i[0];s=[];for(let[v,P]of Object.entries(f.models))P&&s.push({alias:P,provider:f.id,model:P,fallbacks:[]});r={port:3456,host:"localhost"},a=ae(i,s,r,n,e),console.log(`
|
|
21
|
+
${x} Generated configuration:${d}
|
|
22
|
+
`),console.log(se(i,s,r)),console.log(),console.log(a.split(`
|
|
23
|
+
`).map(v=>` ${v}`).join(`
|
|
24
|
+
`));let{confirm:b}=await p({type:"confirm",name:"confirm",message:`[Step 2 of ${u}] Write this configuration?`,initial:!0},g);if(b)break;console.log(`
|
|
25
|
+
Restarting quick setup...
|
|
26
|
+
`)}catch(c){if(c instanceof I){l||(console.log(`
|
|
27
|
+
Setup cancelled. No files were changed.`),process.exit(0));continue}throw c}await ue(i,s,r,a,u,!0,!!t)}async function ue(o,t,n,e,i,s,r){let a=O(process.env.HOME||process.env.USERPROFILE||"",".modelweaver");ie(a,{recursive:!0});let u=O(a,"config.yaml");ne(u)&&r?M(`Updating existing config at ${u}`):M(`Writing new config to ${u}`),oe(u,e),Ae(o);try{let{readPidFile:c,isProcessAlive:f}=await import("./daemon-WP5LZ7DG.js"),b=await c();if(b&&f(b)){if(process.platform!=="win32")try{process.kill(b,"SIGUSR1")}catch{}else console.log(" Windows does not support SIGUSR1 \u2014 run 'modelweaver reload' to pick up new config.");M("ModelWeaver daemon reloaded with new config")}}catch{}let l=await Ee(t);if(l){let c=n.host==="localhost"?`http://localhost:${n.port}`:`http://${n.host}:${n.port}`;J()&&console.log(" Backed up existing settings to settings.json.bak");let b=V(),v=Q(b,{baseUrl:c,defaultModel:l.defaultModel,availableModels:l.availableModels});X(v),M(`Claude Code settings updated at ${z()}`),console.log(` Proxy endpoint: ${c}`),console.log(` Default model: ${l.defaultModel}`),console.log(` Available models: ${l.availableModels.join(", ")}`),console.log(),console.log(` ${re}Restart Claude Code to apply changes.${d}`)}return l}var N=0,F=1,D=2,te=3;async function He(o){if(o?.quick)return Ie();process.stdin.isTTY||(console.error("Error: modelweaver init requires an interactive terminal."),process.exit(1));let{peekConfig:t}=await import("./config-CE7DQIUM.js"),n=t(),e=n?.providers??new Map,i=n?.modelRouting??new Map,s=e.size>0,r=[],a=[],u=[],l={port:3456,host:"localhost"},c,f=null,b=s,v=N;for(;;)try{if(v<=N){if(j(),console.log(`
|
|
28
|
+
${x}${m}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\u2500
|
|
29
|
+
\u2551 Welcome to ModelWeaver! \u2551
|
|
30
|
+
\u2551 \u2551
|
|
31
|
+
\u2551 This wizard will help you configure \u2551
|
|
32
|
+
\u2551 your multi-provider model proxy. \u2551
|
|
33
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${d}
|
|
34
|
+
`),s){console.log(`
|
|
35
|
+
${Pe(e)}
|
|
36
|
+
`);let{action:$}=await p({type:"select",name:"action",message:"What would you like to do?",choices:[{title:"Add new provider(s)",value:"add",description:"Keep existing, add more"},{title:"Edit existing provider",value:"edit",description:"Modify settings of a configured provider"},{title:"Reconfigure all from scratch",value:"reset",description:"Start over (existing providers will be replaced)"}]},g);if($==="add"){let y=new Set(e.keys()),h=await W({excludeIds:y});h.length===0&&console.log(" No additional providers selected. All existing providers preserved.");let k=E(e.size+h.length,!1);r=[];for(let w=0;w<h.length;w++){let _=await T(h[w],{current:1+w,total:k});_&&r.push(_)}}else if($==="edit"){let y=[...e.keys()],{editId:h}=await p({type:"select",name:"editId",message:"Select provider to edit:",choices:[{title:"\u2B05 Go back",value:"__back__"},...y.map(S=>{let R=e.get(S);return{title:S,value:S,description:R.baseUrl}})]},g);if(h==="__back__")continue;let k=e.get(h),w=E(e.size,!1),_=await T(h,{current:1,total:w},k);r=_?[_]:[]}else{e.clear(),i.clear(),b=!1;let y=await W(),h=E(y.length,!1);r=[];for(let k=0;k<y.length;k++){let w=await T(y[k],{current:1+k,total:h});w&&r.push(w)}}r.length===0&&e.size===0&&(console.log(`
|
|
37
|
+
${A}No providers configured. Exiting.${d}
|
|
38
|
+
`),process.exit(1))}else{let $=await W(),y=E($.length,!1);r=[];for(let h=0;h<$.length;h++){let k=await T($[h],{current:1+h,total:y});k&&r.push(k)}r.length===0&&(console.log(`
|
|
39
|
+
${A}No providers configured. Exiting.${d}
|
|
40
|
+
`),process.exit(1))}u=Se(r,e),v=F}if(v<=F){a=await Me(u,b?i:void 0);let{nav:$}=await p({type:"select",name:"nav",message:"Next step:",choices:[{title:"Continue to server configuration",value:"next",description:"Configure port and host"},{title:"Go back to provider configuration",value:"back",description:"Reconfigure providers"}]},g);if($==="back"){v=N;continue}v=D}if(v<=D){console.log();let $=E(u.length,!1),y=u.length+2;l=await Ce({stepInfo:{current:y,total:$}});let{nav:h}=await p({type:"select",name:"nav",message:"Next step:",choices:[{title:"Review and write configuration",value:"next",description:"Confirm and save"},{title:"Go back to model configuration",value:"back",description:"Reconfigure models"}]},g);if(h==="back"){v=F;continue}v=te}if(v<=te){c=ae(u,a,l,e,i),console.log(`
|
|
41
|
+
${x} Generated configuration:${d}
|
|
42
|
+
`),console.log(se(u,a,l)),console.log(),console.log(c.split(`
|
|
43
|
+
`).map(k=>` ${k}`).join(`
|
|
44
|
+
`));let $=E(u.length,!1),y=u.length+3,{confirm:h}=await p({type:"confirm",name:"confirm",message:`[Step ${y} of ${$}] Write this configuration?`,initial:!0},g);if(h)break;console.log(`
|
|
45
|
+
Returning to server configuration...
|
|
46
|
+
`),v=D;continue}}catch($){if($ instanceof I){v===N&&(console.log(`
|
|
47
|
+
Setup cancelled. No files were changed.`),process.exit(0)),v--;continue}throw $}let P=E(r.length,!1);f=await ue(r,a,l,c,P,!1,s),console.log(`
|
|
48
|
+
${x}${m}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
49
|
+
\u2551 ModelWeaver is configured! \u2551
|
|
50
|
+
\u2551 \u2551
|
|
51
|
+
${f?`\u2551 Claude Code settings have been updated. \u2551
|
|
52
|
+
\u2551 \u2551
|
|
53
|
+
\u2551 Just restart Claude Code to get started. \u2551`:`\u2551 To use with Claude Code: \u2551
|
|
54
|
+
\u2551 \u2551
|
|
55
|
+
\u2551 Terminal 1: \u2551
|
|
56
|
+
\u2551 modelweaver \u2551
|
|
57
|
+
\u2551 \u2551
|
|
58
|
+
\u2551 Terminal 2: \u2551
|
|
59
|
+
\u2551 export ANTHROPIC_BASE_URL=\\ \u2551
|
|
60
|
+
\u2551 http://localhost:${String(l.port).padEnd(25)}\u2551
|
|
61
|
+
\u2551 claude \u2551`}
|
|
62
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${d}
|
|
63
|
+
`)}export{He as runInit,Z as testApiKey};
|
|
64
|
+
//# sourceMappingURL=init-V5J734NZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/init.ts","../src/presets.ts","../src/settings.ts"],"sourcesContent":["// src/init.ts — Interactive setup wizard for ModelWeaver\nimport prompts from 'prompts';\nimport { getPresets, getPreset, type ProviderPreset } from './presets.js';\nimport { stringify as stringifyYaml } from 'yaml';\nimport { writeFileSync, existsSync, readFileSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { readSettings, backupSettings, mergeSettings, writeSettings, getSettingsPath } from './settings.js';\nimport net from 'node:net';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface ConfiguredProvider {\n id: string;\n name: string;\n baseUrl: string;\n envKey: string;\n apiKey: string;\n authType: \"anthropic\" | \"bearer\";\n models: Record<string, string>;\n}\n\ninterface ConfiguredModel {\n alias: string; // user-facing name for /model and availableModels\n provider: string; // provider ID\n model: string; // actual model name sent to provider API\n fallbacks: { provider: string; model: string }[];\n}\n\n/** Lightweight representation of a provider found in existing config.yaml.\n * Used for display and pre-fill; no API key resolution or validation. */\ninterface ExistingProvider {\n id: string;\n baseUrl: string;\n envKey: string;\n authType: \"anthropic\" | \"bearer\";\n timeout: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nclass GoBackError extends Error { constructor() { super('__back__'); this.name = 'GoBackError'; } }\n\nconst CANCEL = { onCancel: () => { throw new GoBackError(); } };\n\nconst GREEN = '\\x1B[32m';\nconst RED = '\\x1B[31m';\nconst CYAN = '\\x1B[36m';\nconst BOLD = '\\x1B[1m';\nconst RESET = '\\x1B[0m';\n\nfunction check(msg: string) { console.log(` ${GREEN}\\u2713${RESET} ${msg}`); }\nfunction fail(msg: string) { console.log(` ${RED}\\u2717${RESET} ${msg}`); }\n\n// ---------------------------------------------------------------------------\n// [Improvement 6] clearScreen — separator instead of full clear\n// ---------------------------------------------------------------------------\n\nfunction clearScreen(): void {\n console.log(`\\n${'\\u2500'.repeat(56)}\\n`);\n}\n\n// ---------------------------------------------------------------------------\n// API key test (unchanged)\n// ---------------------------------------------------------------------------\n\nexport async function testApiKey(\n baseUrl: string,\n apiKey: string,\n preset: ProviderPreset,\n): Promise<{ ok: boolean; error?: string }> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5_000);\n\n const headers: Record<string, string> =\n preset.authType === 'anthropic'\n ? { 'x-api-key': apiKey, 'anthropic-version': '2023-06-01', 'anthropic-beta': 'interleaved-thinking-2025-05-14', 'content-type': 'application/json' }\n : { Authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' };\n\n try {\n const res = await fetch(`${baseUrl}${preset.testPath}`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ model: preset.models.sonnet, max_tokens: 1, messages: [{ role: 'user', content: 'hi' }] }),\n signal: controller.signal,\n });\n\n if (res.status === 401 || res.status === 403) return { ok: false, error: 'Invalid API key' };\n if (res.status === 200 || res.status === 400 || res.status === 429) return { ok: true };\n // Check for \"insufficient balance\" — key is valid, account just has no credits\n try {\n const body = await res.json() as { error?: { message?: string } };\n if (body.error?.message?.includes('insufficient balance')) {\n return { ok: true };\n }\n } catch { /* ignore parse errors */ }\n return { ok: false, error: `Unexpected status ${res.status}` };\n } catch (err: unknown) {\n if ((err as Error).name === 'AbortError') return { ok: false, error: 'Request timed out' };\n return { ok: false, error: 'Network error \\u2014 endpoint unreachable' };\n } finally {\n clearTimeout(timeout);\n }\n}\n\n// ---------------------------------------------------------------------------\n// [Improvement 1] detectEnvApiKey — auto-detect existing keys\n// ---------------------------------------------------------------------------\n\nfunction detectEnvApiKey(preset: ProviderPreset): { found: boolean; key: string; source: string } {\n // dotenv.config() already loads ~/.modelweaver/.env into process.env in index.ts\n // before runInit() is called, so a single check suffices.\n const envVal = process.env[preset.envKey];\n if (envVal && envVal.trim()) {\n return { found: true, key: envVal.trim(), source: 'environment' };\n }\n return { found: false, key: '', source: '' };\n}\n\n// ---------------------------------------------------------------------------\n// [Improvement 4] calculateTotalSteps — step counter\n// ---------------------------------------------------------------------------\n\nfunction calculateTotalSteps(selectedProviderCount: number, quick: boolean): number {\n if (quick) {\n return 3;\n }\n // Normal mode: N (configure each provider) + model config (1) + server (1) + confirm (1)\n return selectedProviderCount + 1 + 1 + 1;\n}\n\n// ---------------------------------------------------------------------------\n// [Improvement 7] buildSummaryTable — formatted summary\n// ---------------------------------------------------------------------------\n\nfunction buildSummaryTable(\n providers: ConfiguredProvider[],\n models: ConfiguredModel[],\n server: { port: number; host: string },\n): string {\n const lines: string[] = [];\n const W = 56;\n\n lines.push(`${CYAN}\\u250c${'\\u2500'.repeat(W)}\\u2510${RESET}`);\n lines.push(`${CYAN}\\u2502${RESET}${BOLD} ModelWeaver Configuration Summary${''.padEnd(W - 33)}${CYAN}\\u2502${RESET}`);\n lines.push(`${CYAN}\\u251c${'\\u2500'.repeat(W)}\\u2524${RESET}`);\n lines.push(`${CYAN}\\u2502${RESET} ${BOLD}Server:${RESET} ${server.host}:${server.port}${''.padEnd(W - 8 - `${server.host}:${server.port}`.length)}${CYAN}\\u2502${RESET}`);\n lines.push(`${CYAN}\\u251c${'\\u2500'.repeat(W)}\\u2524${RESET}`);\n\n for (const m of models) {\n const provider = providers.find(p => p.id === m.provider);\n const pName = provider ? provider.name : m.provider;\n const info = `${m.alias.padEnd(16)} ${pName} \\u2192 ${m.model}`;\n lines.push(`${CYAN}\\u2502${RESET} ${info}${''.padEnd(Math.max(0, W - info.length - 2))}${CYAN}\\u2502${RESET}`);\n for (const fb of m.fallbacks) {\n const fbProvider = providers.find(p => p.id === fb.provider);\n const fbPName = fbProvider ? fbProvider.name : fb.provider;\n const fbInfo = ` fallback: ${fbPName} \\u2192 ${fb.model}`;\n lines.push(`${CYAN}\\u2502${RESET} ${fbInfo}${''.padEnd(Math.max(0, W - fbInfo.length - 2))}${CYAN}\\u2502${RESET}`);\n }\n }\n\n lines.push(`${CYAN}\\u2514${'\\u2500'.repeat(W)}\\u2518${RESET}`);\n return lines.join('\\n');\n}\n\n/** Build a formatted table showing existing configured providers. */\nfunction buildExistingProvidersTable(\n existingProviders: Map<string, ExistingProvider>,\n): string {\n const lines: string[] = [];\n const W = 56;\n const providers = [...existingProviders.entries()];\n\n lines.push(`${CYAN}${'\\u250c' + '\\u2500'.repeat(W) + '\\u2510'}${RESET}`);\n lines.push(`${CYAN}\\u2502${RESET}${BOLD} Currently Configured Providers${''.padEnd(W - 30)}${CYAN}\\u2502${RESET}`);\n lines.push(`${CYAN}\\u251c${'\\u2500'.repeat(W)}\\u2524${RESET}`);\n\n for (const [id, p] of providers) {\n lines.push(`${CYAN}\\u2502${RESET} ${BOLD}${id}${RESET}${''.padEnd(Math.max(0, 20 - id.length))} ${p.baseUrl}${''.padEnd(Math.max(0, W - 22 - p.baseUrl.length))}${CYAN}\\u2502${RESET}`);\n const envLabel = p.envKey || '(hardcoded key)';\n const padding = Math.max(0, W - 22 - envLabel.length - p.authType.length);\n lines.push(`${CYAN}\\u2502${RESET} env: ${envLabel} auth: ${p.authType}${''.padEnd(padding)}${CYAN}\\u2502${RESET}`);\n }\n\n lines.push(`${CYAN}${'\\u2514' + '\\u2500'.repeat(W) + '\\u2518'}${RESET}`);\n return lines.join('\\n');\n}\n\n/** Merge freshly configured providers with untouched existing providers.\n * Used for routing selection and summary display. */\nfunction buildAllProviders(\n configured: ConfiguredProvider[],\n existingMap: Map<string, ExistingProvider>,\n): ConfiguredProvider[] {\n const touchedIds = new Set(configured.map(p => p.id));\n const result = [...configured];\n\n for (const [id, ep] of existingMap.entries()) {\n if (!touchedIds.has(id)) {\n const preset = getPreset(id);\n result.push({\n id,\n name: preset?.name ?? id,\n baseUrl: ep.baseUrl,\n envKey: ep.envKey,\n apiKey: \"\",\n authType: ep.authType,\n models: preset?.models ?? { sonnet: \"\", opus: \"\", haiku: \"\" },\n });\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Wizard steps\n// ---------------------------------------------------------------------------\n\n// [Modified] selectProviders — supports singleSelect for --quick mode, excludeIds for add mode\nasync function selectProviders(options?: { singleSelect?: boolean; excludeIds?: Set<string> }): Promise<string[]> {\n let allPresets = getPresets();\n if (options?.excludeIds) {\n allPresets = allPresets.filter(p => !options.excludeIds!.has(p.id));\n }\n\n if (allPresets.length === 0) return [];\n\n if (options?.singleSelect) {\n const { providerId } = await prompts(\n {\n type: 'select',\n name: 'providerId',\n message: 'Select a provider:',\n choices: [\n { title: '\\u2B05 Go back', value: '__back__' },\n ...allPresets.map((p) => ({ title: p.name, value: p.id, description: p.baseUrl })),\n ],\n },\n CANCEL,\n );\n return [providerId as string];\n }\n\n const { providerIds } = await prompts(\n {\n type: 'multiselect',\n name: 'providerIds',\n message: 'Select providers to configure:',\n choices: allPresets.map((p) => ({ title: p.name, value: p.id, description: p.baseUrl })),\n min: 1,\n },\n CANCEL,\n );\n return providerIds as string[];\n}\n\n// [Modified] configureProvider — auto-detect env keys + step counter + existing provider pre-fill\nasync function configureProvider(\n id: string,\n stepInfo?: { current: number; total: number },\n existing?: ExistingProvider,\n): Promise<ConfiguredProvider | null> {\n const preset = getPreset(id);\n if (!preset) {\n console.error(` Error: Unknown provider \"${id}\". Skipping.`);\n return null;\n }\n\n const effectiveEnvKey = existing?.envKey ?? preset.envKey;\n const effectiveBaseUrl = existing?.baseUrl ?? preset.baseUrl;\n const effectiveAuthType = existing?.authType ?? preset.authType;\n\n // [Improvement 1] Auto-detect existing env key — check custom env key first if editing\n let detected = detectEnvApiKey(preset);\n if (existing?.envKey && existing.envKey !== preset.envKey) {\n const customDetected = detectEnvApiKey({ ...preset, envKey: existing.envKey });\n if (customDetected.found) detected = customDetected;\n }\n\n if (detected.found) {\n process.stdout.write(` Testing API key for ${preset.name} (from ${detected.source})...`);\n const result = await testApiKey(effectiveBaseUrl, detected.key, preset);\n process.stdout.write('\\r' + ' '.repeat(60) + '\\r');\n\n if (result.ok) {\n check(`${preset.name}: existing ${effectiveEnvKey} is valid (${detected.source})`);\n const { useExisting } = await prompts(\n { type: 'confirm', name: 'useExisting', message: `Use this key for ${preset.name}?`, initial: true },\n CANCEL,\n );\n if (useExisting) {\n return {\n id, name: preset.name, baseUrl: effectiveBaseUrl,\n envKey: effectiveEnvKey, apiKey: detected.key,\n authType: effectiveAuthType, models: preset.models,\n };\n }\n // User chose to change — fall through to manual prompt below\n console.log(` Enter a new key for ${preset.name}:`);\n }\n\n // Key found but invalid — fall through to prompt\n console.log(` ${RED}\\u26A0${RESET} Existing ${effectiveEnvKey} (${detected.source}) is invalid, please provide a new key.`);\n }\n\n const stepLabel = stepInfo ? `[Step ${stepInfo.current} of ${stepInfo.total}] ` : '';\n\n const MAX_RETRIES = 3;\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n const { baseUrl } = await prompts(\n { type: 'text', name: 'baseUrl', message: `${stepLabel}[${preset.name}] Base URL:`, initial: effectiveBaseUrl },\n CANCEL,\n );\n\n // Validate the base URL format\n try {\n new URL(baseUrl as string);\n } catch {\n console.log(\" Invalid URL format. Please try again.\");\n if (attempt < MAX_RETRIES - 1) continue;\n const { retry } = await prompts(\n { type: 'confirm', name: 'retry', message: `Retry with a valid URL? (${attempt + 1}/${MAX_RETRIES - 1} retries used)`, initial: true },\n CANCEL,\n );\n if (!retry) break;\n continue;\n }\n\n const { apiKey } = await prompts(\n { type: 'password', name: 'apiKey', message: `${stepLabel}[${preset.name}] API key:` },\n CANCEL,\n );\n\n // Spinner-style test\n process.stdout.write(` Testing API key for ${preset.name}...`);\n const result = await testApiKey(baseUrl as string, apiKey as string, preset);\n process.stdout.write('\\r' + ' '.repeat(60) + '\\r');\n\n if (result.ok) {\n check(`${preset.name} API key accepted`);\n return { id, name: preset.name, baseUrl: baseUrl as string, envKey: effectiveEnvKey, apiKey: apiKey as string, authType: effectiveAuthType, models: preset.models };\n }\n\n fail(`${preset.name}: ${result.error}`);\n\n if (attempt < MAX_RETRIES - 1) {\n const { retry } = await prompts(\n { type: 'confirm', name: 'retry', message: `Retry? (${attempt + 1}/${MAX_RETRIES - 1} retries used)`, initial: true },\n CANCEL,\n );\n if (!retry) break;\n }\n }\n\n console.log(` ${RED}Warning:${RESET} ${preset.name} will be skipped \\u2014 max retries (${MAX_RETRIES}) exceeded.`);\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Fallback configuration helper\n// ---------------------------------------------------------------------------\n\n/** Prompt the user to add fallback providers for a model. Returns the fallbacks array. */\nasync function configureFallbacks(\n alias: string,\n primaryProviderId: string,\n providers: ConfiguredProvider[],\n existingFallbacks: { provider: string; model: string }[] = [],\n): Promise<{ provider: string; model: string }[]> {\n const fallbacks = [...existingFallbacks];\n\n while (true) {\n try {\n const { addFallback } = await prompts(\n {\n type: 'confirm',\n name: 'addFallback',\n message: fallbacks.length === 0\n ? `Add a fallback provider for ${alias}?`\n : `Add another fallback provider for ${alias}?`,\n initial: false,\n },\n CANCEL,\n );\n\n if (!addFallback) break;\n\n const availableProviders = providers.filter(p => p.id !== primaryProviderId);\n if (availableProviders.length === 0) {\n console.log(` ${RED}No other providers available for fallback.${RESET}`);\n break;\n }\n\n const providerChoices = availableProviders.map((p) => ({ title: p.name, value: p.id }));\n const { providerId } = await prompts(\n {\n type: 'select',\n name: 'providerId',\n message: `Select fallback provider for ${alias}:`,\n choices: [\n { title: '\\u2B05 Go back', value: '__back__' },\n ...providerChoices,\n ],\n },\n CANCEL,\n );\n\n if (providerId === '__back__') continue;\n\n const selectedProvider = providers.find((p) => p.id === (providerId as string))!;\n const { modelName } = await prompts(\n {\n type: 'text',\n name: 'modelName',\n message: `[${selectedProvider.name}] Fallback model name:`,\n initial: '',\n },\n CANCEL,\n );\n\n fallbacks.push({\n provider: selectedProvider.id,\n model: (modelName as string).trim(),\n });\n\n check(`Added fallback: ${selectedProvider.id} \\u2192 ${(modelName as string).trim()}`);\n } catch (err) {\n if (err instanceof GoBackError) break;\n throw err;\n }\n }\n\n return fallbacks;\n}\n\n// ---------------------------------------------------------------------------\n// N-model configuration\n// ---------------------------------------------------------------------------\n\n/** Build a formatted table showing existing modelRouting entries. */\nfunction buildExistingModelsTable(\n modelRouting: Map<string, { provider: string; model: string }[]>,\n): string {\n const lines: string[] = [];\n const W = 56;\n const entries = [...modelRouting.entries()];\n\n lines.push(`${CYAN}${'\\u250c' + '\\u2500'.repeat(W) + '\\u2510'}${RESET}`);\n lines.push(`${CYAN}\\u2502${RESET}${BOLD} Currently Configured Models${''.padEnd(W - 28)}${CYAN}\\u2502${RESET}`);\n lines.push(`${CYAN}\\u251c${'\\u2500'.repeat(W)}\\u2524${RESET}`);\n\n if (entries.length === 0) {\n lines.push(`${CYAN}\\u2502${RESET} (none)${''.padEnd(W - 7)}${CYAN}\\u2502${RESET}`);\n } else {\n for (const [alias, chain] of entries) {\n const primary = chain[0];\n const info = `${alias.padEnd(20)} ${primary.provider} \\u2192 ${primary.model}`;\n lines.push(`${CYAN}\\u2502${RESET} ${info}${''.padEnd(Math.max(0, W - info.length - 2))}${CYAN}\\u2502${RESET}`);\n for (let i = 1; i < chain.length; i++) {\n const fbInfo = ` fallback: ${chain[i].provider} \\u2192 ${chain[i].model}`;\n lines.push(`${CYAN}\\u2502${RESET} ${fbInfo}${''.padEnd(Math.max(0, W - fbInfo.length - 2))}${CYAN}\\u2502${RESET}`);\n }\n }\n }\n\n lines.push(`${CYAN}${'\\u2514' + '\\u2500'.repeat(W) + '\\u2518'}${RESET}`);\n return lines.join('\\n');\n}\n\nasync function configureModels(\n providers: ConfiguredProvider[],\n existingModels?: Map<string, { provider: string; model: string }[]>,\n): Promise<ConfiguredModel[]> {\n const models: ConfiguredModel[] = [];\n const hasExisting = existingModels && existingModels.size > 0;\n\n // Seed models from existing modelRouting\n if (hasExisting) {\n for (const [alias, chain] of existingModels!.entries()) {\n const primary = chain[0];\n models.push({\n alias,\n provider: primary.provider,\n model: primary.model,\n fallbacks: chain.slice(1),\n });\n }\n }\n\n console.log();\n console.log(' Configure models \\u2014 each model gets an alias that appears in Claude Code\\'s /model picker.');\n console.log(' The proxy will route requests matching the alias to the chosen provider.');\n console.log();\n\n if (hasExisting) {\n // Editing existing config: show current models, then offer add/edit/delete menu\n while (true) {\n console.log(buildExistingModelsTable(existingModels!));\n console.log();\n\n const { action } = await prompts({\n type: 'select',\n name: 'action',\n message: 'What would you like to do?',\n choices: [\n { title: 'Add new model', value: 'add', description: 'Add a model alias routed to a provider' },\n ...(models.length > 0 ? [{ title: 'Edit existing model', value: 'edit', description: 'Change alias, provider, or model name' }] : []),\n ...(models.length > 0 ? [{ title: 'Delete model', value: 'delete', description: 'Remove a model alias' }] : []),\n { title: 'Done', value: 'done', description: 'Continue to server configuration' },\n ],\n }, CANCEL);\n\n if (action === 'done') break;\n\n if (action === 'add') {\n const providerChoices = providers.map((p) => ({ title: p.name, value: p.id }));\n const { providerId } = await prompts(\n {\n type: 'select',\n name: 'providerId',\n message: 'Select provider:',\n choices: [\n { title: '\\u2B05 Go back', value: '__back__' },\n ...providerChoices,\n ],\n },\n CANCEL,\n );\n\n if (providerId === '__back__') continue;\n\n const selectedProvider = providers.find((p) => p.id === (providerId as string))!;\n const { alias } = await prompts(\n {\n type: 'text',\n name: 'alias',\n message: `[${selectedProvider.name}] Model alias (name in /model picker):`,\n initial: '',\n },\n CANCEL,\n );\n\n const { modelName } = await prompts(\n {\n type: 'text',\n name: 'modelName',\n message: `[${selectedProvider.name}] Actual model name (sent to provider API):`,\n initial: alias as string,\n },\n CANCEL,\n );\n\n const newModel: ConfiguredModel = {\n alias: (alias as string).trim(),\n provider: selectedProvider.id,\n model: (modelName as string).trim(),\n fallbacks: [],\n };\n\n // Configure fallbacks for this model\n newModel.fallbacks = await configureFallbacks(\n newModel.alias, newModel.provider, providers, newModel.fallbacks,\n );\n\n models.push(newModel);\n // Update the existing map so the table stays current (including fallbacks)\n existingModels!.set(newModel.alias, [\n { provider: newModel.provider, model: newModel.model },\n ...newModel.fallbacks,\n ]);\n check(`Added model: ${newModel.alias}`);\n }\n\n if (action === 'edit') {\n const modelChoices = models.map((m) => ({\n title: m.alias,\n value: m.alias,\n description: `${m.provider} \\u2192 ${m.model}`,\n }));\n const { editAlias } = await prompts({\n type: 'select',\n name: 'editAlias',\n message: 'Select model to edit:',\n choices: [\n { title: '\\u2B05 Go back', value: '__back__' },\n ...modelChoices,\n ],\n }, CANCEL);\n\n if (editAlias === '__back__') continue;\n\n const idx = models.findIndex((m) => m.alias === editAlias);\n if (idx === -1) continue;\n\n const current = models[idx];\n let currentFallbacks = [...current.fallbacks];\n\n // If alias changed, remove old entry from existing map\n const { alias } = await prompts(\n {\n type: 'text',\n name: 'alias',\n message: `Model alias:`,\n initial: current.alias,\n },\n CANCEL,\n );\n\n const providerChoices = providers.map((p) => ({ title: p.name, value: p.id }));\n const { providerId } = await prompts(\n {\n type: 'select',\n name: 'providerId',\n message: 'Select provider:',\n choices: [\n { title: '\\u2B05 Go back', value: '__back__' },\n ...providerChoices,\n ],\n },\n CANCEL,\n );\n\n if (providerId === '__back__') continue;\n\n const selectedProvider = providers.find((p) => p.id === (providerId as string))!;\n const { modelName } = await prompts(\n {\n type: 'text',\n name: 'modelName',\n message: `Actual model name:`,\n initial: current.model,\n },\n CANCEL,\n );\n\n const newAlias = (alias as string).trim();\n const newModel = (modelName as string).trim();\n\n // Fallback management loop\n while (true) {\n // Show current fallbacks\n if (currentFallbacks.length === 0) {\n console.log(` ${CYAN}No fallbacks configured.${RESET}`);\n } else {\n console.log(` ${CYAN}Current fallbacks:${RESET}`);\n for (let i = 0; i < currentFallbacks.length; i++) {\n const fb = currentFallbacks[i];\n const fbProvider = providers.find(p => p.id === fb.provider);\n const fbPName = fbProvider ? fbProvider.name : fb.provider;\n console.log(` ${i + 1}. ${fbPName} \\u2192 ${fb.model}`);\n }\n }\n console.log();\n\n const fallbackActions = [\n { title: 'Add fallback', value: 'add_fb' },\n ...(currentFallbacks.length > 0 ? [{ title: 'Remove fallback', value: 'remove_fb' }] : []),\n { title: 'Done editing fallbacks', value: 'done_fb' },\n ];\n\n const { fallbackAction } = await prompts({\n type: 'select',\n name: 'fallbackAction',\n message: 'Manage fallbacks:',\n choices: fallbackActions,\n }, CANCEL);\n\n if (fallbackAction === 'done_fb') break;\n\n if (fallbackAction === 'add_fb') {\n const newFb = await configureFallbacks(\n newAlias, selectedProvider.id, providers, currentFallbacks,\n );\n currentFallbacks = newFb;\n }\n\n if (fallbackAction === 'remove_fb') {\n const fbChoices = currentFallbacks.map((fb, i) => {\n const fbProvider = providers.find(p => p.id === fb.provider);\n const fbPName = fbProvider ? fbProvider.name : fb.provider;\n return { title: `${fbPName} \\u2192 ${fb.model}`, value: i };\n });\n const { removeIdx } = await prompts({\n type: 'select',\n name: 'removeIdx',\n message: 'Select fallback to remove:',\n choices: [\n { title: '\\u2B05 Go back', value: '__back__' },\n ...fbChoices,\n ],\n }, CANCEL);\n\n if (removeIdx !== '__back__') {\n const removed = currentFallbacks.splice(removeIdx as number, 1);\n check(`Removed fallback: ${removed[0].provider} \\u2192 ${removed[0].model}`);\n }\n }\n }\n\n // If alias changed, remove old entry from existing map\n if (newAlias !== current.alias) {\n existingModels!.delete(current.alias);\n }\n\n models[idx] = {\n alias: newAlias,\n provider: selectedProvider.id,\n model: newModel,\n fallbacks: currentFallbacks,\n };\n\n // Update existing map with full chain including fallbacks\n existingModels!.set(newAlias, [\n { provider: selectedProvider.id, model: newModel },\n ...currentFallbacks,\n ]);\n\n check(`Updated model: ${newAlias} (${selectedProvider.id} \\u2192 ${newModel})`);\n }\n\n if (action === 'delete') {\n const modelChoices = models.map((m) => ({\n title: m.alias,\n value: m.alias,\n description: `${m.provider} \\u2192 ${m.model}`,\n }));\n const { deleteAlias } = await prompts({\n type: 'select',\n name: 'deleteAlias',\n message: 'Select model to delete:',\n choices: [\n { title: '\\u2B05 Go back', value: '__back__' },\n ...modelChoices,\n ],\n }, CANCEL);\n\n if (deleteAlias === '__back__') continue;\n\n const idx = models.findIndex((m) => m.alias === deleteAlias);\n if (idx !== -1) {\n models.splice(idx, 1);\n existingModels!.delete(deleteAlias as string);\n check(`Deleted model: ${deleteAlias as string}`);\n }\n }\n\n clearScreen();\n }\n } else {\n // Fresh setup: auto-suggest one model per provider using preset defaults\n for (const provider of providers) {\n const { addModel } = await prompts(\n {\n type: 'confirm',\n name: 'addModel',\n message: `Add a model for ${provider.name}?`,\n initial: true,\n },\n CANCEL,\n );\n\n if (addModel) {\n const presetModel = provider.models?.sonnet || provider.models?.opus || provider.models?.haiku || '';\n const { alias } = await prompts(\n {\n type: 'text',\n name: 'alias',\n message: `[${provider.name}] Model alias (name in /model picker):`,\n initial: presetModel || provider.id,\n },\n CANCEL,\n );\n\n const { modelName } = await prompts(\n {\n type: 'text',\n name: 'modelName',\n message: `[${provider.name}] Actual model name (sent to provider API):`,\n initial: presetModel,\n },\n CANCEL,\n );\n\n const newModel: ConfiguredModel = {\n alias: (alias as string).trim(),\n provider: provider.id,\n model: (modelName as string).trim(),\n fallbacks: [],\n };\n\n // Configure fallbacks for this model\n newModel.fallbacks = await configureFallbacks(\n newModel.alias, newModel.provider, providers, newModel.fallbacks,\n );\n\n models.push(newModel);\n }\n }\n\n // Allow adding additional models\n while (true) {\n console.log();\n const { addMore } = await prompts(\n {\n type: 'confirm',\n name: 'addMore',\n message: models.length === 0\n ? 'Add a model?'\n : 'Add another model?',\n initial: models.length === 0,\n },\n CANCEL,\n );\n\n if (!addMore) break;\n\n const providerChoices = providers.map((p) => ({ title: p.name, value: p.id }));\n const { providerId } = await prompts(\n {\n type: 'select',\n name: 'providerId',\n message: 'Select provider:',\n choices: providerChoices,\n },\n CANCEL,\n );\n\n const selectedProvider = providers.find((p) => p.id === (providerId as string))!;\n const { alias } = await prompts(\n {\n type: 'text',\n name: 'alias',\n message: `[${selectedProvider.name}] Model alias:`,\n initial: '',\n },\n CANCEL,\n );\n\n const { modelName } = await prompts(\n {\n type: 'text',\n name: 'modelName',\n message: `[${selectedProvider.name}] Actual model name:`,\n initial: alias as string,\n },\n CANCEL,\n );\n\n const newModel: ConfiguredModel = {\n alias: (alias as string).trim(),\n provider: selectedProvider.id,\n model: (modelName as string).trim(),\n fallbacks: [],\n };\n\n // Configure fallbacks for this model\n newModel.fallbacks = await configureFallbacks(\n newModel.alias, newModel.provider, providers, newModel.fallbacks,\n );\n\n models.push(newModel);\n }\n }\n\n if (models.length === 0) {\n console.log(' No models configured.');\n } else {\n check(`${models.length} model(s) configured`);\n }\n\n return models;\n}\n\n// ---------------------------------------------------------------------------\n// Port collision detection\n// ---------------------------------------------------------------------------\n\nfunction isPortInUse(port: number, host: string): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer();\n server.once('error', () => { server.close(); resolve(true); });\n server.once('listening', () => { server.close(); resolve(false); });\n server.listen(port, host);\n });\n}\n\n// [Modified] configureServer — supports useDefaults for --quick mode\nasync function configureServer(\n options?: { useDefaults?: boolean; stepInfo?: { current: number; total: number } },\n): Promise<{ port: number; host: string }> {\n if (options?.useDefaults) {\n return { port: 3456, host: 'localhost' };\n }\n\n const stepLabel = options?.stepInfo ? `[Step ${options.stepInfo.current} of ${options.stepInfo.total}] ` : '';\n\n const { port } = await prompts(\n { type: 'number', name: 'port', message: `${stepLabel}Server port:`, initial: 3456 },\n CANCEL,\n );\n const { host } = await prompts(\n { type: 'text', name: 'host', message: `${stepLabel}Server host:`, initial: 'localhost' },\n CANCEL,\n );\n\n // Port collision check\n if (await isPortInUse(port as number, host as string)) {\n console.log(` ${RED}\\u26A0 Warning:${RESET} Port ${port} is already in use on ${host}.`);\n const { proceed } = await prompts(\n { type: 'confirm', name: 'proceed', message: 'Use this port anyway?', initial: false },\n CANCEL,\n );\n if (!proceed) {\n console.log(' Please choose a different port and try again.');\n process.exit(1);\n }\n }\n\n return { port: port as number, host: host as string };\n}\n\ninterface SettingsConfig {\n defaultModel: string;\n availableModels: string[];\n}\n\nasync function configureClaudeCodeSettings(\n models: ConfiguredModel[],\n): Promise<SettingsConfig | null> {\n if (models.length === 0) return null;\n\n try {\n console.log();\n\n const { configure } = await prompts(\n {\n type: 'confirm',\n name: 'configure',\n message: 'Configure Claude Code to use ModelWeaver automatically?',\n initial: true,\n },\n CANCEL,\n );\n\n if (!configure) return null;\n\n const { defaultModel } = await prompts(\n {\n type: 'select',\n name: 'defaultModel',\n message: 'Select default model for Claude Code:',\n choices: models.map((m) => ({\n title: m.alias,\n description: `${m.provider} \\u2192 ${m.model}`,\n value: m.alias,\n })),\n },\n CANCEL,\n );\n\n const availableModels = models.map((m) => m.alias);\n\n return { defaultModel: defaultModel as string, availableModels };\n } catch (err) {\n if (err instanceof GoBackError) return null;\n throw err;\n }\n}\n\nfunction buildYamlConfig(\n providers: ConfiguredProvider[],\n models: ConfiguredModel[],\n server: { port: number; host: string },\n existingProviders?: Map<string, ExistingProvider>,\n existingModelRouting?: Map<string, { provider: string; model: string }[]>,\n): string {\n // Build modelRouting from configured models (primary + fallbacks)\n const modelRouting: Record<string, { provider: string; model: string }[]> = {};\n for (const m of models) {\n modelRouting[m.alias] = [\n { provider: m.provider, model: m.model },\n ...m.fallbacks,\n ];\n }\n // For any models in existing routing that are NOT in the current models list,\n // preserve them as-is (backward compatibility)\n if (existingModelRouting) {\n for (const [alias, chain] of existingModelRouting.entries()) {\n if (!modelRouting[alias]) {\n modelRouting[alias] = chain.map(e => ({ provider: e.provider, model: e.model }));\n }\n }\n }\n\n const configObj: {\n server: { port: number; host: string };\n providers: Record<string, Record<string, unknown>>;\n modelRouting: Record<string, { provider: string; model: string }[]>;\n } = {\n server,\n providers: {},\n modelRouting,\n };\n\n for (const p of providers) {\n const providerConfig: Record<string, unknown> = {\n baseUrl: p.baseUrl,\n apiKey: `\\${${p.envKey}}`,\n timeout: 30000,\n };\n if (p.authType === \"bearer\") {\n providerConfig.authType = \"bearer\";\n }\n configObj.providers[p.id] = providerConfig;\n }\n\n // Merge in untouched existing providers (preserved verbatim)\n if (existingProviders) {\n const touchedIds = new Set(providers.map(p => p.id));\n for (const [id, ep] of existingProviders.entries()) {\n if (!touchedIds.has(id)) {\n const providerConfig: Record<string, unknown> = {\n baseUrl: ep.baseUrl,\n apiKey: ep.envKey ? `\\${${ep.envKey}}` : \"\",\n timeout: ep.timeout,\n };\n if (ep.authType === \"bearer\") {\n providerConfig.authType = \"bearer\";\n }\n configObj.providers[id] = providerConfig;\n }\n }\n }\n\n return stringifyYaml(configObj);\n}\n\nfunction writeEnvFile(entries: ConfiguredProvider[]): void {\n const envDir = join(process.env.HOME || process.env.USERPROFILE || '', '.modelweaver');\n const envPath = join(envDir, '.env');\n mkdirSync(envDir, { recursive: true });\n let existing = '';\n\n if (existsSync(envPath)) {\n existing = readFileSync(envPath, 'utf-8');\n }\n\n if (existing && !existing.endsWith('\\n')) existing += '\\n';\n\n const lines: string[] = [];\n\n for (const entry of entries) {\n // Escape regex metacharacters in env key name\n const escapedKey = entry.envKey.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const regex = new RegExp(`^${escapedKey}=.*$`, 'm');\n const quotedValue = entry.apiKey.includes('\"')\n ? (entry.apiKey.includes(\"'\") ? `'${entry.apiKey.replace(/'/g, \"'\\\\''\")}'` : `'${entry.apiKey}'`)\n : `\"${entry.apiKey}\"`;\n if (regex.test(existing)) {\n existing = existing.replace(regex, `${entry.envKey}=${quotedValue}`);\n } else {\n lines.push(`${entry.envKey}=${quotedValue}`);\n }\n }\n\n\n writeFileSync(envPath, existing + lines.join('\\n') + (lines.length > 0 ? '\\n' : ''), { mode: 0o600 });\n}\n\n// ---------------------------------------------------------------------------\n// [Improvement 3] runQuickInit — --quick / -q express mode\n// ---------------------------------------------------------------------------\n\nasync function runQuickInit(): Promise<void> {\n // Step 0 — TTY check\n if (!process.stdin.isTTY) {\n console.error('Error: modelweaver init --quick requires an interactive terminal.');\n process.exit(1);\n }\n\n // Peek at existing config for merge support\n const { peekConfig } = await import('./config.js');\n const existingPeek = peekConfig();\n const existingProviderMap = existingPeek?.providers ?? new Map();\n const existingModelRouting = existingPeek?.modelRouting ?? new Map();\n\n let configured: ConfiguredProvider[] = [];\n let configuredModels: ConfiguredModel[] = [];\n let server: { port: number; host: string };\n let yaml: string;\n\n const totalSteps = calculateTotalSteps(1, true);\n\n let pastFirstStep = false;\n\n while (true) {\n try {\n // Welcome\n clearScreen();\n console.log(`\n${BOLD}${CYAN}\\u250C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2510\\u2500\n\\u2502 Welcome to ModelWeaver! \\u2501 Quick Setup \\u2502\n\\u2502 \\u2502\n\\u2501 ~${String(totalSteps).padEnd(3)} quick steps to get started \\u2501\n\\u2514\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2518${RESET}\n`);\n\n // Step 1: Select single provider\n const selectedIds = await selectProviders({ singleSelect: true });\n pastFirstStep = true;\n\n if (selectedIds.length === 1 && selectedIds[0] === '__back__') {\n continue; // Go back — restart quick setup loop\n }\n\n // Step 2: Configure provider (auto-detects env key)\n configured = [];\n for (const id of selectedIds) {\n const provider = await configureProvider(id, { current: 1, total: totalSteps });\n if (provider) configured.push(provider);\n }\n\n if (configured.length === 0) {\n console.log(`\\n ${RED}No providers configured. Exiting.${RESET}\\n`);\n process.exit(1);\n }\n\n // Auto-configure models from single provider's presets\n const provider = configured[0];\n configuredModels = [];\n for (const [tier, modelName] of Object.entries(provider.models)) {\n if (modelName) {\n configuredModels.push({ alias: modelName, provider: provider.id, model: modelName, fallbacks: [] });\n }\n }\n\n // Server defaults (no prompts)\n server = { port: 3456, host: 'localhost' };\n\n // Step 3: Summary + Confirm\n yaml = buildYamlConfig(configured, configuredModels, server, existingProviderMap, existingModelRouting);\n console.log(`\\n${BOLD} Generated configuration:${RESET}\\n`);\n console.log(buildSummaryTable(configured, configuredModels, server));\n console.log();\n console.log(yaml.split('\\n').map((l) => ` ${l}`).join('\\n'));\n\n const { confirm } = await prompts(\n { type: 'confirm', name: 'confirm', message: `[Step 2 of ${totalSteps}] Write this configuration?`, initial: true },\n CANCEL,\n );\n\n if (confirm) break;\n\n console.log('\\n Restarting quick setup...\\n');\n } catch (err) {\n if (err instanceof GoBackError) {\n if (!pastFirstStep) {\n console.log('\\n Setup cancelled. No files were changed.');\n process.exit(0);\n }\n continue;\n }\n throw err;\n }\n }\n\n // Write files\n await writeConfigAndSettings(configured, configuredModels, server, yaml, totalSteps, true, !!existingPeek);\n}\n\n// ---------------------------------------------------------------------------\n// Shared file-writing + settings logic\n// ---------------------------------------------------------------------------\n\nasync function writeConfigAndSettings(\n configured: ConfiguredProvider[],\n models: ConfiguredModel[],\n server: { port: number; host: string },\n yaml: string,\n totalSteps: number,\n quick?: boolean,\n hasExisting?: boolean,\n): Promise<SettingsConfig | null> {\n const modelweaverDir = join(process.env.HOME || process.env.USERPROFILE || '', '.modelweaver');\n mkdirSync(modelweaverDir, { recursive: true });\n const configPath = join(modelweaverDir, 'config.yaml');\n if (existsSync(configPath) && hasExisting) {\n check(`Updating existing config at ${configPath}`);\n } else {\n check(`Writing new config to ${configPath}`);\n }\n writeFileSync(configPath, yaml);\n writeEnvFile(configured);\n\n // Signal daemon to reload config if it's running\n try {\n const { readPidFile, isProcessAlive } = await import('./daemon.js');\n const pid = await readPidFile();\n if (pid && isProcessAlive(pid)) {\n if (process.platform !== \"win32\") {\n try { process.kill(pid, 'SIGUSR1'); } catch { /* process may not exist */ }\n } else {\n console.log(\" Windows does not support SIGUSR1 \\u2014 run 'modelweaver reload' to pick up new config.\");\n }\n check('ModelWeaver daemon reloaded with new config');\n }\n } catch {\n // Daemon not running or daemon.js not available — silently ignore\n }\n\n // Configure Claude Code settings.json\n const result = await configureClaudeCodeSettings(models);\n\n if (result) {\n const baseUrl = server.host === 'localhost'\n ? `http://localhost:${server.port}`\n : `http://${server.host}:${server.port}`;\n\n const didBackup = backupSettings();\n if (didBackup) {\n console.log(` Backed up existing settings to settings.json.bak`);\n }\n\n const existing = readSettings();\n const merged = mergeSettings(existing, {\n baseUrl,\n defaultModel: result.defaultModel,\n availableModels: result.availableModels,\n });\n writeSettings(merged);\n\n check(`Claude Code settings updated at ${getSettingsPath()}`);\n console.log(` Proxy endpoint: ${baseUrl}`);\n console.log(` Default model: ${result.defaultModel}`);\n console.log(` Available models: ${result.availableModels.join(', ')}`);\n console.log();\n console.log(` ${GREEN}Restart Claude Code to apply changes.${RESET}`);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Main wizard (normal mode)\n// ---------------------------------------------------------------------------\n\n// Phase constants for the phase-based loop\nconst PHASE_PROVIDERS = 0;\nconst PHASE_MODELS = 1;\nconst PHASE_SERVER = 2;\nconst PHASE_CONFIRM = 3;\n\nexport async function runInit(options?: { quick?: boolean }): Promise<void> {\n if (options?.quick) {\n return runQuickInit();\n }\n\n // Step 0 — TTY check\n if (!process.stdin.isTTY) {\n console.error('Error: modelweaver init requires an interactive terminal.');\n process.exit(1);\n }\n\n // Peek at existing config for provider detection + merge\n const { peekConfig } = await import('./config.js');\n const existingPeek = peekConfig();\n const existingProviderMap = existingPeek?.providers ?? new Map();\n const existingModelRouting = existingPeek?.modelRouting ?? new Map();\n const hasExisting = existingProviderMap.size > 0;\n\n // State that persists across phases (go-back preserves these)\n let configured: ConfiguredProvider[] = [];\n let configuredModels: ConfiguredModel[] = [];\n let allProviders: ConfiguredProvider[] = [];\n let server: { port: number; host: string } = { port: 3456, host: 'localhost' };\n let yaml: string;\n let settingsConfig: SettingsConfig | null = null;\n let useExistingModels = hasExisting;\n\n let phase = PHASE_PROVIDERS;\n\n while (true) {\n try {\n // ── PHASE: Provider configuration ──\n if (phase <= PHASE_PROVIDERS) {\n // Step 1 — Welcome\n clearScreen();\n console.log(`\n${BOLD}${CYAN}\\u2554\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2557\\u2500\n\\u2551 Welcome to ModelWeaver! \\u2551\n\\u2551 \\u2551\n\\u2551 This wizard will help you configure \\u2551\n\\u2551 your multi-provider model proxy. \\u2551\n\\u255A\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u255D${RESET}\n`);\n\n if (hasExisting) {\n // Show existing providers + action menu\n console.log(`\\n${buildExistingProvidersTable(existingProviderMap)}\\n`);\n\n const { action } = await prompts({\n type: 'select',\n name: 'action',\n message: 'What would you like to do?',\n choices: [\n { title: 'Add new provider(s)', value: 'add', description: 'Keep existing, add more' },\n { title: 'Edit existing provider', value: 'edit', description: 'Modify settings of a configured provider' },\n { title: 'Reconfigure all from scratch', value: 'reset', description: 'Start over (existing providers will be replaced)' },\n ],\n }, CANCEL);\n\n if (action === 'add') {\n const excludeIds = new Set(existingProviderMap.keys());\n const selectedIds = await selectProviders({ excludeIds });\n if (selectedIds.length === 0) {\n console.log(' No additional providers selected. All existing providers preserved.');\n }\n const totalSteps = calculateTotalSteps(\n existingProviderMap.size + selectedIds.length, false\n );\n configured = [];\n for (let i = 0; i < selectedIds.length; i++) {\n const provider = await configureProvider(\n selectedIds[i],\n { current: 1 + i, total: totalSteps }\n );\n if (provider) configured.push(provider);\n }\n } else if (action === 'edit') {\n const existingIds = [...existingProviderMap.keys()];\n const { editId } = await prompts({\n type: 'select',\n name: 'editId',\n message: 'Select provider to edit:',\n choices: [\n { title: '\\u2B05 Go back', value: '__back__' },\n ...existingIds.map(id => {\n const p = existingProviderMap.get(id)!;\n return { title: id, value: id, description: p.baseUrl };\n }),\n ],\n }, CANCEL);\n if (editId === '__back__') {\n continue; // Go back to the main action menu\n }\n const existing = existingProviderMap.get(editId as string)!;\n const totalSteps = calculateTotalSteps(existingProviderMap.size, false);\n const provider = await configureProvider(\n editId as string,\n { current: 1, total: totalSteps },\n existing,\n );\n configured = provider ? [provider] : [];\n } else {\n // 'reset' — fresh start, clear existing map so nothing gets merged\n existingProviderMap.clear();\n existingModelRouting.clear();\n useExistingModels = false;\n const selectedIds = await selectProviders();\n const totalSteps = calculateTotalSteps(selectedIds.length, false);\n configured = [];\n for (let i = 0; i < selectedIds.length; i++) {\n const provider = await configureProvider(selectedIds[i], { current: 1 + i, total: totalSteps });\n if (provider) configured.push(provider);\n }\n }\n\n if (configured.length === 0 && (existingProviderMap.size === 0)) {\n console.log(`\\n ${RED}No providers configured. Exiting.${RESET}\\n`);\n process.exit(1);\n }\n } else {\n // ── FRESH SETUP (original flow, no changes) ──\n const selectedIds = await selectProviders();\n const totalSteps = calculateTotalSteps(selectedIds.length, false);\n configured = [];\n for (let i = 0; i < selectedIds.length; i++) {\n const provider = await configureProvider(selectedIds[i], { current: 1 + i, total: totalSteps });\n if (provider) configured.push(provider);\n }\n\n if (configured.length === 0) {\n console.log(`\\n ${RED}No providers configured. Exiting.${RESET}\\n`);\n process.exit(1);\n }\n }\n\n // Build the full provider list: configured (new/edited) + untouched existing\n allProviders = buildAllProviders(configured, existingProviderMap);\n phase = PHASE_MODELS;\n }\n\n // ── PHASE: Model configuration ──\n if (phase <= PHASE_MODELS) {\n // Configure models (N-model flow)\n configuredModels = await configureModels(allProviders, useExistingModels ? existingModelRouting : undefined);\n\n // Navigation prompt: continue to server config or go back to providers\n const { nav } = await prompts({\n type: 'select',\n name: 'nav',\n message: 'Next step:',\n choices: [\n { title: 'Continue to server configuration', value: 'next', description: 'Configure port and host' },\n { title: 'Go back to provider configuration', value: 'back', description: 'Reconfigure providers' },\n ],\n }, CANCEL);\n\n if (nav === 'back') {\n phase = PHASE_PROVIDERS;\n continue;\n }\n\n phase = PHASE_SERVER;\n }\n\n // ── PHASE: Server configuration ──\n if (phase <= PHASE_SERVER) {\n // Server config\n console.log();\n const totalSteps = calculateTotalSteps(allProviders.length, false);\n const serverStep = allProviders.length + 2; // providers + models + server\n server = await configureServer({ stepInfo: { current: serverStep, total: totalSteps } });\n\n // Navigation prompt: continue to review or go back to models\n const { nav } = await prompts({\n type: 'select',\n name: 'nav',\n message: 'Next step:',\n choices: [\n { title: 'Review and write configuration', value: 'next', description: 'Confirm and save' },\n { title: 'Go back to model configuration', value: 'back', description: 'Reconfigure models' },\n ],\n }, CANCEL);\n\n if (nav === 'back') {\n phase = PHASE_MODELS;\n continue;\n }\n\n phase = PHASE_CONFIRM;\n }\n\n // ── PHASE: Review & confirm ──\n if (phase <= PHASE_CONFIRM) {\n // Review & confirm\n yaml = buildYamlConfig(allProviders, configuredModels, server, existingProviderMap, existingModelRouting);\n console.log(`\\n${BOLD} Generated configuration:${RESET}\\n`);\n console.log(buildSummaryTable(allProviders, configuredModels, server));\n console.log();\n console.log(yaml.split('\\n').map((l) => ` ${l}`).join('\\n'));\n\n const totalSteps = calculateTotalSteps(allProviders.length, false);\n const confirmStep = allProviders.length + 3; // providers + models + server + confirm\n const { confirm } = await prompts(\n { type: 'confirm', name: 'confirm', message: `[Step ${confirmStep} of ${totalSteps}] Write this configuration?`, initial: true },\n CANCEL,\n );\n\n if (confirm) break;\n\n // Go back to server configuration instead of restarting the entire wizard\n console.log('\\n Returning to server configuration...\\n');\n phase = PHASE_SERVER;\n continue;\n }\n } catch (err) {\n if (err instanceof GoBackError) {\n if (phase === PHASE_PROVIDERS) {\n console.log('\\n Setup cancelled. No files were changed.');\n process.exit(0);\n }\n phase--;\n continue;\n }\n throw err;\n }\n }\n\n // Write files + settings\n const finalTotalSteps = calculateTotalSteps(configured.length, false);\n settingsConfig = await writeConfigAndSettings(configured, configuredModels, server, yaml, finalTotalSteps, false, hasExisting);\n\n // Step 8 — Success banner\n console.log(`\n${BOLD}${CYAN}\\u2554\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2557\n\\u2551 ModelWeaver is configured! \\u2551\n\\u2551 \\u2551\n${settingsConfig\n ? `\\u2551 Claude Code settings have been updated. \\u2551\n\\u2551 \\u2551\n\\u2551 Just restart Claude Code to get started. \\u2551`\n : `\\u2551 To use with Claude Code: \\u2551\n\\u2551 \\u2551\n\\u2551 Terminal 1: \\u2551\n\\u2551 modelweaver \\u2551\n\\u2551 \\u2551\n\\u2551 Terminal 2: \\u2551\n\\u2551 export ANTHROPIC_BASE_URL=\\\\ \\u2551\n\\u2551 http://localhost:${String(server.port).padEnd(25)}\\u2551\n\\u2551 claude \\u2551`\n}\n\\u255A\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u2550\\u255D${RESET}\n`);\n}\n","/**\n * Provider preset templates for the ModelWeaver init wizard.\n */\n\nexport interface ProviderPreset {\n /** Machine name for config keys (e.g., \"anthropic\") */\n id: string;\n /** Display name (e.g., \"Anthropic\") */\n name: string;\n /** API base URL */\n baseUrl: string;\n /** Suggested env var name (e.g., \"ANTHROPIC_API_KEY\") */\n envKey: string;\n /** How to send the API key */\n authType: \"bearer\" | \"anthropic\";\n /** API endpoint path used for key validation */\n testPath: string;\n models: {\n sonnet: string;\n opus: string;\n haiku: string;\n };\n}\n\nconst PRESETS: ProviderPreset[] = [\n {\n id: \"anthropic\",\n name: \"Anthropic\",\n baseUrl: \"https://api.anthropic.com\",\n envKey: \"ANTHROPIC_API_KEY\",\n authType: \"anthropic\",\n testPath: \"/v1/messages\",\n models: {\n sonnet: \"claude-sonnet-4-20250514\",\n opus: \"claude-opus-4-20250514\",\n haiku: \"claude-haiku-4-5-20251001\",\n },\n },\n {\n id: \"openrouter\",\n name: \"OpenRouter\",\n baseUrl: \"https://openrouter.ai/api\",\n envKey: \"OPENROUTER_API_KEY\",\n authType: \"bearer\",\n testPath: \"/v1/chat/completions\",\n models: {\n sonnet: \"anthropic/claude-sonnet-4\",\n opus: \"anthropic/claude-opus-4\",\n haiku: \"anthropic/claude-haiku-4\",\n },\n },\n {\n id: \"together\",\n name: \"Together AI\",\n baseUrl: \"https://api.together.xyz\",\n envKey: \"TOGETHER_API_KEY\",\n authType: \"bearer\",\n testPath: \"/v1/chat/completions\",\n models: {\n sonnet: \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n opus: \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n haiku: \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n },\n },\n {\n id: \"glm\",\n name: \"GLM (Z.ai)\",\n baseUrl: \"https://api.z.ai/api/anthropic\",\n envKey: \"GLM_API_KEY\",\n authType: \"anthropic\",\n testPath: \"/v1/messages\",\n models: {\n sonnet: \"claude-sonnet-4-20250514\",\n opus: \"claude-opus-4-20250514\",\n haiku: \"claude-haiku-4-5-20251001\",\n },\n },\n {\n id: \"minimax\",\n name: \"Minimax\",\n baseUrl: \"https://api.minimax.io/anthropic\",\n envKey: \"MINIMAX_API_KEY\",\n authType: \"anthropic\",\n testPath: \"/v1/messages\",\n models: {\n sonnet: \"MiniMax-M2.7\",\n opus: \"MiniMax-M2.7\",\n haiku: \"MiniMax-M2.7\",\n },\n },\n {\n id: \"fireworks\",\n name: \"Fireworks\",\n baseUrl: \"https://api.fireworks.ai/inference/v1\",\n envKey: \"FIREWORKS_API_KEY\",\n authType: \"bearer\",\n testPath: \"/chat/completions\",\n models: {\n sonnet: \"accounts/fireworks/models/claude-sonnet-4\",\n opus: \"accounts/fireworks/models/claude-opus-4\",\n haiku: \"accounts/fireworks/models/claude-haiku-4\",\n },\n },\n];\n\n/** Returns all available provider presets. */\nexport function getPresets(): ProviderPreset[] {\n return PRESETS;\n}\n\n/** Returns a single preset by its machine-readable id, or undefined if not found. */\nexport function getPreset(id: string): ProviderPreset | undefined {\n return PRESETS.find((p) => p.id === id);\n}\n","// src/settings.ts — Read/write/merge Claude Code settings.json\nimport { readFileSync, writeFileSync, existsSync, copyFileSync, mkdirSync, renameSync, unlinkSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// --- Types ---\n\nexport interface ClaudeSettings {\n env?: Record<string, string>;\n model?: string;\n [key: string]: unknown;\n}\n\nexport interface SettingsWriteOptions {\n baseUrl: string; // e.g., \"http://localhost:3456\"\n defaultModel?: string; // top-level model override\n tierModels?: { // tier alias overrides\n sonnet?: string;\n opus?: string;\n haiku?: string;\n };\n availableModels?: string[]; // model aliases for /model picker\n}\n\n// --- Paths ---\n\nconst CLAUDE_DIR = join(homedir(), \".claude\");\nconst SETTINGS_PATH = join(CLAUDE_DIR, \"settings.json\");\nconst BACKUP_PATH = join(CLAUDE_DIR, \"settings.json.bak\");\n\n// --- Public API ---\n\nexport function getSettingsPath(): string {\n return SETTINGS_PATH;\n}\n\n/**\n * Read ~/.claude/settings.json. Returns empty object if file doesn't exist.\n */\nexport function readSettings(): ClaudeSettings {\n if (!existsSync(SETTINGS_PATH)) return {};\n const raw = readFileSync(SETTINGS_PATH, \"utf-8\");\n try {\n return JSON.parse(raw) as ClaudeSettings;\n } catch (err) {\n // Log the error so the user knows settings failed to parse\n console.error(\"[settings] Failed to parse settings.json:\", err);\n throw err;\n }\n}\n\n/**\n * Backup existing settings.json to settings.json.bak.\n * Returns true if backup was created, false if no file to backup.\n */\nexport function backupSettings(): boolean {\n if (!existsSync(SETTINGS_PATH)) return false;\n console.log(\"[settings] Backing up existing settings to .bak\");\n copyFileSync(SETTINGS_PATH, BACKUP_PATH);\n return true;\n}\n\n/**\n * Merge model-routing fields into existing settings, preserving everything else.\n *\n * Strategy:\n * - Deep-merge `env` (overwrite only our keys, leave user's keys untouched)\n * - Set top-level `model` only if provided\n * - Preserve all other top-level keys (permissions, hooks, etc.)\n */\nexport function mergeSettings(\n existing: ClaudeSettings,\n options: SettingsWriteOptions\n): ClaudeSettings {\n const result: ClaudeSettings = { ...existing };\n\n // Deep-merge env\n result.env = { ...(existing.env || {}) };\n\n // Our keys to set\n const envKeys: Record<string, string> = {\n ANTHROPIC_BASE_URL: options.baseUrl,\n };\n\n for (const [key, value] of Object.entries(envKeys)) {\n result.env[key] = value;\n }\n\n // Tier alias overrides (only set if provided)\n if (options.tierModels) {\n const tierEnvMap: Record<string, string> = {\n sonnet: \"ANTHROPIC_DEFAULT_SONNET_MODEL\",\n opus: \"ANTHROPIC_DEFAULT_OPUS_MODEL\",\n haiku: \"ANTHROPIC_DEFAULT_HAIKU_MODEL\",\n };\n for (const [tier, envKey] of Object.entries(tierEnvMap)) {\n const modelValue = options.tierModels[tier as keyof typeof options.tierModels];\n if (modelValue) {\n result.env[envKey] = modelValue;\n }\n }\n }\n\n // Top-level model override\n if (options.defaultModel) {\n result.model = options.defaultModel;\n }\n\n // availableModels — set if provided, otherwise leave untouched\n if (options.availableModels && options.availableModels.length > 0) {\n result.availableModels = options.availableModels;\n }\n\n return result;\n}\n\n/**\n * Write settings to ~/.claude/settings.json.\n * Creates the directory if it doesn't exist.\n */\nexport function writeSettings(settings: ClaudeSettings): void {\n mkdirSync(dirname(SETTINGS_PATH), { recursive: true });\n const tmpPath = SETTINGS_PATH + \".tmp\";\n try {\n writeFileSync(tmpPath, JSON.stringify(settings, null, 2) + \"\\n\", \"utf-8\");\n renameSync(tmpPath, SETTINGS_PATH);\n } catch (err) {\n // Log the error so the user knows settings failed to write\n console.error(\"[settings] Failed to write settings.json:\", err);\n // Clean up temp file if it exists\n try { unlinkSync(tmpPath); } catch { /* ignore */ }\n throw err;\n }\n}\n"],"mappings":"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","names":["prompts","PRESETS","getPresets","getPreset","id","p","stringifyYaml","writeFileSync","existsSync","readFileSync","mkdirSync","join","readFileSync","writeFileSync","existsSync","copyFileSync","mkdirSync","renameSync","unlinkSync","join","dirname","homedir","CLAUDE_DIR","SETTINGS_PATH","BACKUP_PATH","getSettingsPath","readSettings","raw","err","backupSettings","mergeSettings","existing","options","result","envKeys","key","value","tierEnvMap","tier","envKey","modelValue","writeSettings","settings","tmpPath","net","GoBackError","CANCEL","GREEN","RED","CYAN","BOLD","RESET","check","msg","fail","clearScreen","testApiKey","baseUrl","apiKey","preset","controller","timeout","headers","res","err","detectEnvApiKey","envVal","calculateTotalSteps","selectedProviderCount","quick","buildSummaryTable","providers","models","server","lines","m","provider","p","pName","info","fb","fbProvider","fbInfo","buildExistingProvidersTable","existingProviders","id","envLabel","padding","buildAllProviders","configured","existingMap","touchedIds","result","ep","getPreset","selectProviders","options","allPresets","getPresets","providerId","prompts","providerIds","configureProvider","stepInfo","existing","effectiveEnvKey","effectiveBaseUrl","effectiveAuthType","detected","customDetected","useExisting","stepLabel","MAX_RETRIES","attempt","retry","configureFallbacks","alias","primaryProviderId","existingFallbacks","fallbacks","addFallback","availableProviders","providerChoices","selectedProvider","modelName","buildExistingModelsTable","modelRouting","entries","chain","primary","i","configureModels","existingModels","hasExisting","action","newModel","modelChoices","editAlias","idx","current","currentFallbacks","newAlias","fbPName","fallbackActions","fallbackAction","fbChoices","removeIdx","removed","deleteAlias","addModel","presetModel","addMore","isPortInUse","port","host","resolve","configureServer","proceed","configureClaudeCodeSettings","configure","defaultModel","availableModels","buildYamlConfig","existingModelRouting","e","configObj","providerConfig","stringifyYaml","writeEnvFile","envDir","join","envPath","mkdirSync","existsSync","readFileSync","entry","escapedKey","regex","quotedValue","writeFileSync","runQuickInit","peekConfig","existingPeek","existingProviderMap","configuredModels","yaml","totalSteps","pastFirstStep","selectedIds","tier","l","confirm","writeConfigAndSettings","modelweaverDir","configPath","readPidFile","isProcessAlive","pid","backupSettings","readSettings","merged","mergeSettings","writeSettings","getSettingsPath","PHASE_PROVIDERS","PHASE_MODELS","PHASE_SERVER","PHASE_CONFIRM","runInit","allProviders","settingsConfig","useExistingModels","phase","excludeIds","existingIds","editId","nav","serverStep","confirmStep","finalTotalSteps"]}
|
package/package.json
CHANGED
package/dist/config-P34YQCFG.js
DELETED
package/dist/init-VLTKSOZN.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import p from"prompts";var j=[{id:"anthropic",name:"Anthropic",baseUrl:"https://api.anthropic.com",envKey:"ANTHROPIC_API_KEY",authType:"anthropic",testPath:"/v1/messages",models:{sonnet:"claude-sonnet-4-20250514",opus:"claude-opus-4-20250514",haiku:"claude-haiku-4-5-20251001"}},{id:"openrouter",name:"OpenRouter",baseUrl:"https://openrouter.ai/api",envKey:"OPENROUTER_API_KEY",authType:"bearer",testPath:"/v1/chat/completions",models:{sonnet:"anthropic/claude-sonnet-4",opus:"anthropic/claude-opus-4",haiku:"anthropic/claude-haiku-4"}},{id:"together",name:"Together AI",baseUrl:"https://api.together.xyz",envKey:"TOGETHER_API_KEY",authType:"bearer",testPath:"/v1/chat/completions",models:{sonnet:"meta-llama/Llama-3.3-70B-Instruct-Turbo",opus:"meta-llama/Llama-3.3-70B-Instruct-Turbo",haiku:"meta-llama/Llama-3.3-70B-Instruct-Turbo"}},{id:"glm",name:"GLM (Z.ai)",baseUrl:"https://api.z.ai/api/anthropic",envKey:"GLM_API_KEY",authType:"anthropic",testPath:"/v1/messages",models:{sonnet:"claude-sonnet-4-20250514",opus:"claude-opus-4-20250514",haiku:"claude-haiku-4-5-20251001"}},{id:"minimax",name:"Minimax",baseUrl:"https://api.minimax.io/anthropic",envKey:"MINIMAX_API_KEY",authType:"anthropic",testPath:"/v1/messages",models:{sonnet:"MiniMax-M2.7",opus:"MiniMax-M2.7",haiku:"MiniMax-M2.7"}},{id:"fireworks",name:"Fireworks",baseUrl:"https://api.fireworks.ai/inference/v1",envKey:"FIREWORKS_API_KEY",authType:"bearer",testPath:"/chat/completions",models:{sonnet:"accounts/fireworks/models/claude-sonnet-4",opus:"accounts/fireworks/models/claude-opus-4",haiku:"accounts/fireworks/models/claude-haiku-4"}}];function G(){return j}function W(i){return j.find(t=>t.id===i)}import{stringify as $e}from"yaml";import{writeFileSync as te,existsSync as oe,readFileSync as be,mkdirSync as ne}from"fs";import{join as N}from"path";import{readFileSync as le,writeFileSync as ce,existsSync as H,copyFileSync as de,mkdirSync as pe,renameSync as ge,unlinkSync as me}from"fs";import{join as O,dirname as fe}from"path";import{homedir as ve}from"os";var q=O(ve(),".claude"),C=O(q,"settings.json"),he=O(q,"settings.json.bak");function Y(){return C}function z(){if(!H(C))return{};let i=le(C,"utf-8");try{return JSON.parse(i)}catch(t){throw console.error("[settings] Failed to parse settings.json:",t),t}}function V(){return H(C)?(console.log("[settings] Backing up existing settings to .bak"),de(C,he),!0):!1}function J(i,t){let o={...i};o.env={...i.env||{}};let e={ANTHROPIC_BASE_URL:t.baseUrl};for(let[n,s]of Object.entries(e))o.env[n]=s;if(t.tierModels){let n={sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL"};for(let[s,r]of Object.entries(n)){let a=t.tierModels[s];a&&(o.env[r]=a)}}return t.defaultModel&&(o.model=t.defaultModel),t.availableModels&&t.availableModels.length>0&&(o.availableModels=t.availableModels),o}function Q(i){pe(fe(C),{recursive:!0});let t=C+".tmp";try{ce(t,JSON.stringify(i,null,2)+`
|
|
3
|
-
`,"utf-8"),ge(t,C)}catch(o){console.error("[settings] Failed to write settings.json:",o);try{me(t)}catch{}throw o}}import ye from"net";var g={onCancel:()=>{console.log(`
|
|
4
|
-
Setup cancelled. No files were changed.`),process.exit(0)}},ie="\x1B[32m",E="\x1B[31m",m="\x1B[36m",M="\x1B[1m",d="\x1B[0m";function _(i){console.log(` ${ie}\u2713${d} ${i}`)}function ke(i){console.log(` ${E}\u2717${d} ${i}`)}function F(){console.log(`
|
|
5
|
-
${"\u2500".repeat(56)}
|
|
6
|
-
`)}async function X(i,t,o){let e=new AbortController,n=setTimeout(()=>e.abort(),5e3),s=o.authType==="anthropic"?{"x-api-key":t,"anthropic-version":"2023-06-01","anthropic-beta":"interleaved-thinking-2025-05-14","content-type":"application/json"}:{Authorization:`Bearer ${t}`,"content-type":"application/json"};try{let r=await fetch(`${i}${o.testPath}`,{method:"POST",headers:s,body:JSON.stringify({model:o.models.sonnet,max_tokens:1,messages:[{role:"user",content:"hi"}]}),signal:e.signal});if(r.status===401||r.status===403)return{ok:!1,error:"Invalid API key"};if(r.status===200||r.status===400||r.status===429)return{ok:!0};try{if((await r.json()).error?.message?.includes("insufficient balance"))return{ok:!0}}catch{}return{ok:!1,error:`Unexpected status ${r.status}`}}catch(r){return r.name==="AbortError"?{ok:!1,error:"Request timed out"}:{ok:!1,error:"Network error \u2014 endpoint unreachable"}}finally{clearTimeout(n)}}function Z(i){let t=process.env[i.envKey];return t&&t.trim()?{found:!0,key:t.trim(),source:"environment"}:{found:!1,key:"",source:""}}function x(i,t){return t?3:i+1+1+1}function re(i,t,o){let e=[];e.push(`${m}\u250C${"\u2500".repeat(56)}\u2510${d}`),e.push(`${m}\u2502${d}${M} ModelWeaver Configuration Summary${"".padEnd(23)}${m}\u2502${d}`),e.push(`${m}\u251C${"\u2500".repeat(56)}\u2524${d}`),e.push(`${m}\u2502${d} ${M}Server:${d} ${o.host}:${o.port}${"".padEnd(48-`${o.host}:${o.port}`.length)}${m}\u2502${d}`),e.push(`${m}\u251C${"\u2500".repeat(56)}\u2524${d}`);for(let s of t){let r=i.find(l=>l.id===s.provider),a=r?r.name:s.provider,u=`${s.alias.padEnd(16)} ${a} \u2192 ${s.model}`;e.push(`${m}\u2502${d} ${u}${"".padEnd(Math.max(0,56-u.length-2))}${m}\u2502${d}`);for(let l of s.fallbacks){let c=i.find(h=>h.id===l.provider),$=` fallback: ${c?c.name:l.provider} \u2192 ${l.model}`;e.push(`${m}\u2502${d} ${$}${"".padEnd(Math.max(0,56-$.length-2))}${m}\u2502${d}`)}}return e.push(`${m}\u2514${"\u2500".repeat(56)}\u2518${d}`),e.join(`
|
|
7
|
-
`)}function we(i){let t=[],e=[...i.entries()];t.push(`${m}${"\u250C"+"\u2500".repeat(56)+"\u2510"}${d}`),t.push(`${m}\u2502${d}${M} Currently Configured Providers${"".padEnd(26)}${m}\u2502${d}`),t.push(`${m}\u251C${"\u2500".repeat(56)}\u2524${d}`);for(let[n,s]of e){t.push(`${m}\u2502${d} ${M}${n}${d}${"".padEnd(Math.max(0,20-n.length))} ${s.baseUrl}${"".padEnd(Math.max(0,34-s.baseUrl.length))}${m}\u2502${d}`);let r=s.envKey||"(hardcoded key)",a=Math.max(0,34-r.length-s.authType.length);t.push(`${m}\u2502${d} env: ${r} auth: ${s.authType}${"".padEnd(a)}${m}\u2502${d}`)}return t.push(`${m}${"\u2514"+"\u2500".repeat(56)+"\u2518"}${d}`),t.join(`
|
|
8
|
-
`)}function Pe(i,t){let o=new Set(i.map(n=>n.id)),e=[...i];for(let[n,s]of t.entries())if(!o.has(n)){let r=W(n);e.push({id:n,name:r?.name??n,baseUrl:s.baseUrl,envKey:s.envKey,apiKey:"",authType:s.authType,models:r?.models??{sonnet:"",opus:"",haiku:""}})}return e}async function U(i){let t=G();if(i?.excludeIds&&(t=t.filter(e=>!i.excludeIds.has(e.id))),t.length===0)return[];if(i?.singleSelect){let{providerId:e}=await p({type:"select",name:"providerId",message:"Select a provider:",choices:[{title:"\u2B05 Go back",value:"__back__"},...t.map(n=>({title:n.name,value:n.id,description:n.baseUrl}))]},g);return[e]}let{providerIds:o}=await p({type:"multiselect",name:"providerIds",message:"Select providers to configure:",choices:t.map(e=>({title:e.name,value:e.id,description:e.baseUrl})),min:1},g);return o}async function R(i,t,o){let e=W(i);if(!e)return console.error(` Error: Unknown provider "${i}". Skipping.`),null;let n=o?.envKey??e.envKey,s=o?.baseUrl??e.baseUrl,r=o?.authType??e.authType,a=Z(e);if(o?.envKey&&o.envKey!==e.envKey){let c=Z({...e,envKey:o.envKey});c.found&&(a=c)}if(a.found){process.stdout.write(` Testing API key for ${e.name} (from ${a.source})...`);let c=await X(s,a.key,e);if(process.stdout.write("\r"+" ".repeat(60)+"\r"),c.ok)return _(`${e.name}: using existing ${n} (${a.source})`),{id:i,name:e.name,baseUrl:s,envKey:n,apiKey:a.key,authType:r,models:e.models};console.log(` ${E}\u26A0${d} Existing ${n} (${a.source}) is invalid, please provide a new key.`)}let u=t?`[Step ${t.current} of ${t.total}] `:"",l=3;for(let c=0;c<l;c++){let{baseUrl:v}=await p({type:"text",name:"baseUrl",message:`${u}[${e.name}] Base URL:`,initial:s},g);try{new URL(v)}catch{if(console.log(" Invalid URL format. Please try again."),c<l-1)continue;let{retry:A}=await p({type:"confirm",name:"retry",message:`Retry with a valid URL? (${c+1}/${l-1} retries used)`,initial:!0},g);if(!A)break;continue}let{apiKey:$}=await p({type:"password",name:"apiKey",message:`${u}[${e.name}] API key:`},g);process.stdout.write(` Testing API key for ${e.name}...`);let h=await X(v,$,e);if(process.stdout.write("\r"+" ".repeat(60)+"\r"),h.ok)return _(`${e.name} API key accepted`),{id:i,name:e.name,baseUrl:v,envKey:n,apiKey:$,authType:r,models:e.models};if(ke(`${e.name}: ${h.error}`),c<l-1){let{retry:A}=await p({type:"confirm",name:"retry",message:`Retry? (${c+1}/${l-1} retries used)`,initial:!0},g);if(!A)break}}return console.log(` ${E}Warning:${d} ${e.name} will be skipped \u2014 max retries (${l}) exceeded.`),null}async function K(i,t,o,e=[]){let n=[...e];for(;;){let{addFallback:s}=await p({type:"confirm",name:"addFallback",message:n.length===0?`Add a fallback provider for ${i}?`:`Add another fallback provider for ${i}?`,initial:!1},g);if(!s)break;let r=o.filter(v=>v.id!==t);if(r.length===0){console.log(` ${E}No other providers available for fallback.${d}`);break}let a=r.map(v=>({title:v.name,value:v.id})),{providerId:u}=await p({type:"select",name:"providerId",message:`Select fallback provider for ${i}:`,choices:[{title:"\u2B05 Go back",value:"__back__"},...a]},g);if(u==="__back__")continue;let l=o.find(v=>v.id===u),{modelName:c}=await p({type:"text",name:"modelName",message:`[${l.name}] Fallback model name:`,initial:""},g);n.push({provider:l.id,model:c.trim()}),_(`Added fallback: ${l.id} \u2192 ${c.trim()}`)}return n}function Se(i){let t=[],e=[...i.entries()];if(t.push(`${m}${"\u250C"+"\u2500".repeat(56)+"\u2510"}${d}`),t.push(`${m}\u2502${d}${M} Currently Configured Models${"".padEnd(28)}${m}\u2502${d}`),t.push(`${m}\u251C${"\u2500".repeat(56)}\u2524${d}`),e.length===0)t.push(`${m}\u2502${d} (none)${"".padEnd(49)}${m}\u2502${d}`);else for(let[n,s]of e){let r=s[0],a=`${n.padEnd(20)} ${r.provider} \u2192 ${r.model}`;t.push(`${m}\u2502${d} ${a}${"".padEnd(Math.max(0,56-a.length-2))}${m}\u2502${d}`);for(let u=1;u<s.length;u++){let l=` fallback: ${s[u].provider} \u2192 ${s[u].model}`;t.push(`${m}\u2502${d} ${l}${"".padEnd(Math.max(0,56-l.length-2))}${m}\u2502${d}`)}}return t.push(`${m}${"\u2514"+"\u2500".repeat(56)+"\u2518"}${d}`),t.join(`
|
|
9
|
-
`)}async function _e(i,t){let o=[],e=t&&t.size>0;if(e)for(let[n,s]of t.entries()){let r=s[0];o.push({alias:n,provider:r.provider,model:r.model,fallbacks:s.slice(1)})}if(console.log(),console.log(" Configure models \u2014 each model gets an alias that appears in Claude Code's /model picker."),console.log(" The proxy will route requests matching the alias to the chosen provider."),console.log(),e)for(;;){console.log(Se(t)),console.log();let{action:n}=await p({type:"select",name:"action",message:"What would you like to do?",choices:[{title:"Add new model",value:"add",description:"Add a model alias routed to a provider"},...o.length>0?[{title:"Edit existing model",value:"edit",description:"Change alias, provider, or model name"}]:[],...o.length>0?[{title:"Delete model",value:"delete",description:"Remove a model alias"}]:[],{title:"Done",value:"done",description:"Continue to server configuration"}]},g);if(n==="done")break;if(n==="add"){let s=i.map(v=>({title:v.name,value:v.id})),{providerId:r}=await p({type:"select",name:"providerId",message:"Select provider:",choices:[{title:"\u2B05 Go back",value:"__back__"},...s]},g);if(r==="__back__")continue;let a=i.find(v=>v.id===r),{alias:u}=await p({type:"text",name:"alias",message:`[${a.name}] Model alias (name in /model picker):`,initial:""},g),{modelName:l}=await p({type:"text",name:"modelName",message:`[${a.name}] Actual model name (sent to provider API):`,initial:u},g),c={alias:u.trim(),provider:a.id,model:l.trim(),fallbacks:[]};c.fallbacks=await K(c.alias,c.provider,i,c.fallbacks),o.push(c),t.set(c.alias,[{provider:c.provider,model:c.model},...c.fallbacks]),_(`Added model: ${c.alias}`)}if(n==="edit"){let s=o.map(f=>({title:f.alias,value:f.alias,description:`${f.provider} \u2192 ${f.model}`})),{editAlias:r}=await p({type:"select",name:"editAlias",message:"Select model to edit:",choices:[{title:"\u2B05 Go back",value:"__back__"},...s]},g);if(r==="__back__")continue;let a=o.findIndex(f=>f.alias===r);if(a===-1)continue;let u=o[a],l=[...u.fallbacks],{alias:c}=await p({type:"text",name:"alias",message:"Model alias:",initial:u.alias},g),v=i.map(f=>({title:f.name,value:f.id})),{providerId:$}=await p({type:"select",name:"providerId",message:"Select provider:",choices:[{title:"\u2B05 Go back",value:"__back__"},...v]},g);if($==="__back__")continue;let h=i.find(f=>f.id===$),{modelName:A}=await p({type:"text",name:"modelName",message:"Actual model name:",initial:u.model},g),b=c.trim(),y=A.trim();for(;;){if(l.length===0)console.log(` ${m}No fallbacks configured.${d}`);else{console.log(` ${m}Current fallbacks:${d}`);for(let w=0;w<l.length;w++){let S=l[w],P=i.find(T=>T.id===S.provider),I=P?P.name:S.provider;console.log(` ${w+1}. ${I} \u2192 ${S.model}`)}}console.log();let f=[{title:"Add fallback",value:"add_fb"},...l.length>0?[{title:"Remove fallback",value:"remove_fb"}]:[],{title:"Done editing fallbacks",value:"done_fb"}],{fallbackAction:k}=await p({type:"select",name:"fallbackAction",message:"Manage fallbacks:",choices:f},g);if(k==="done_fb")break;if(k==="add_fb"&&(l=await K(b,h.id,i,l)),k==="remove_fb"){let w=l.map((P,I)=>{let T=i.find(ue=>ue.id===P.provider);return{title:`${T?T.name:P.provider} \u2192 ${P.model}`,value:I}}),{removeIdx:S}=await p({type:"select",name:"removeIdx",message:"Select fallback to remove:",choices:[{title:"\u2B05 Go back",value:"__back__"},...w]},g);if(S!=="__back__"){let P=l.splice(S,1);_(`Removed fallback: ${P[0].provider} \u2192 ${P[0].model}`)}}}b!==u.alias&&t.delete(u.alias),o[a]={alias:b,provider:h.id,model:y,fallbacks:l},t.set(b,[{provider:h.id,model:y},...l]),_(`Updated model: ${b} (${h.id} \u2192 ${y})`)}if(n==="delete"){let s=o.map(u=>({title:u.alias,value:u.alias,description:`${u.provider} \u2192 ${u.model}`})),{deleteAlias:r}=await p({type:"select",name:"deleteAlias",message:"Select model to delete:",choices:[{title:"\u2B05 Go back",value:"__back__"},...s]},g);if(r==="__back__")continue;let a=o.findIndex(u=>u.alias===r);a!==-1&&(o.splice(a,1),t.delete(r),_(`Deleted model: ${r}`))}F()}else{for(let n of i){let{addModel:s}=await p({type:"confirm",name:"addModel",message:`Add a model for ${n.name}?`,initial:!0},g);if(s){let r=n.models?.sonnet||n.models?.opus||n.models?.haiku||"",{alias:a}=await p({type:"text",name:"alias",message:`[${n.name}] Model alias (name in /model picker):`,initial:r||n.id},g),{modelName:u}=await p({type:"text",name:"modelName",message:`[${n.name}] Actual model name (sent to provider API):`,initial:r},g),l={alias:a.trim(),provider:n.id,model:u.trim(),fallbacks:[]};l.fallbacks=await K(l.alias,l.provider,i,l.fallbacks),o.push(l)}}for(;;){console.log();let{addMore:n}=await p({type:"confirm",name:"addMore",message:o.length===0?"Add a model?":"Add another model?",initial:o.length===0},g);if(!n)break;let s=i.map(v=>({title:v.name,value:v.id})),{providerId:r}=await p({type:"select",name:"providerId",message:"Select provider:",choices:s},g),a=i.find(v=>v.id===r),{alias:u}=await p({type:"text",name:"alias",message:`[${a.name}] Model alias:`,initial:""},g),{modelName:l}=await p({type:"text",name:"modelName",message:`[${a.name}] Actual model name:`,initial:u},g),c={alias:u.trim(),provider:a.id,model:l.trim(),fallbacks:[]};c.fallbacks=await K(c.alias,c.provider,i,c.fallbacks),o.push(c)}}return o.length===0?console.log(" No models configured."):_(`${o.length} model(s) configured`),o}function Me(i,t){return new Promise(o=>{let e=ye.createServer();e.once("error",()=>{e.close(),o(!0)}),e.once("listening",()=>{e.close(),o(!1)}),e.listen(i,t)})}async function Ce(i){if(i?.useDefaults)return{port:3456,host:"localhost"};let t=i?.stepInfo?`[Step ${i.stepInfo.current} of ${i.stepInfo.total}] `:"",{port:o}=await p({type:"number",name:"port",message:`${t}Server port:`,initial:3456},g),{host:e}=await p({type:"text",name:"host",message:`${t}Server host:`,initial:"localhost"},g);if(await Me(o,e)){console.log(` ${E}\u26A0 Warning:${d} Port ${o} is already in use on ${e}.`);let{proceed:n}=await p({type:"confirm",name:"proceed",message:"Use this port anyway?",initial:!1},g);n||(console.log(" Please choose a different port and try again."),process.exit(1))}return{port:o,host:e}}async function xe(i){if(i.length===0)return null;console.log();let{configure:t}=await p({type:"confirm",name:"configure",message:"Configure Claude Code to use ModelWeaver automatically?",initial:!0},g);if(!t)return null;let{defaultModel:o}=await p({type:"select",name:"defaultModel",message:"Select default model for Claude Code:",choices:i.map(n=>({title:n.alias,description:`${n.provider} \u2192 ${n.model}`,value:n.alias}))},g),e=i.map(n=>n.alias);return{defaultModel:o,availableModels:e}}function se(i,t,o,e,n){let s={};for(let a of t)s[a.alias]=[{provider:a.provider,model:a.model},...a.fallbacks];if(n)for(let[a,u]of n.entries())s[a]||(s[a]=u.map(l=>({provider:l.provider,model:l.model})));let r={server:o,providers:{},modelRouting:s};for(let a of i){let u={baseUrl:a.baseUrl,apiKey:`\${${a.envKey}}`,timeout:3e4};a.authType==="bearer"&&(u.authType="bearer"),r.providers[a.id]=u}if(e){let a=new Set(i.map(u=>u.id));for(let[u,l]of e.entries())if(!a.has(u)){let c={baseUrl:l.baseUrl,apiKey:l.envKey?`\${${l.envKey}}`:"",timeout:l.timeout};l.authType==="bearer"&&(c.authType="bearer"),r.providers[u]=c}}return $e(r)}function Ee(i){let t=N(process.env.HOME||process.env.USERPROFILE||"",".modelweaver"),o=N(t,".env");ne(t,{recursive:!0});let e="";oe(o)&&(e=be(o,"utf-8")),e&&!e.endsWith(`
|
|
10
|
-
`)&&(e+=`
|
|
11
|
-
`);let n=[];for(let s of i){let r=s.envKey.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^${r}=.*$`,"m"),u=s.apiKey.includes('"')?s.apiKey.includes("'")?`'${s.apiKey.replace(/'/g,"'\\''")}'`:`'${s.apiKey}'`:`"${s.apiKey}"`;a.test(e)?e=e.replace(a,`${s.envKey}=${u}`):n.push(`${s.envKey}=${u}`)}te(o,e+n.join(`
|
|
12
|
-
`)+(n.length>0?`
|
|
13
|
-
`:""),{mode:384})}async function Ae(){process.stdin.isTTY||(console.error("Error: modelweaver init --quick requires an interactive terminal."),process.exit(1));let{peekConfig:i}=await import("./config-P34YQCFG.js"),t=i(),o=t?.providers??new Map,e=t?.modelRouting??new Map,n=[],s=[],r,a,u=x(1,!0);for(;;){F(),console.log(`
|
|
14
|
-
${M}${m}\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u2500
|
|
15
|
-
\u2502 Welcome to ModelWeaver! \u2501 Quick Setup \u2502
|
|
16
|
-
\u2502 \u2502
|
|
17
|
-
\u2501 ~${String(u).padEnd(3)} quick steps to get started \u2501
|
|
18
|
-
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518${d}
|
|
19
|
-
`);let l=await U({singleSelect:!0});if(l.length===1&&l[0]==="__back__")continue;n=[];for(let $ of l){let h=await R($,{current:1,total:u});h&&n.push(h)}n.length===0&&(console.log(`
|
|
20
|
-
${E}No providers configured. Exiting.${d}
|
|
21
|
-
`),process.exit(1));let c=n[0];s=[];for(let[$,h]of Object.entries(c.models))h&&s.push({alias:h,provider:c.id,model:h,fallbacks:[]});r={port:3456,host:"localhost"},a=se(n,s,r,o,e),console.log(`
|
|
22
|
-
${M} Generated configuration:${d}
|
|
23
|
-
`),console.log(re(n,s,r)),console.log(),console.log(a.split(`
|
|
24
|
-
`).map($=>` ${$}`).join(`
|
|
25
|
-
`));let{confirm:v}=await p({type:"confirm",name:"confirm",message:`[Step 2 of ${u}] Write this configuration?`,initial:!0},g);if(v)break;console.log(`
|
|
26
|
-
Restarting quick setup...
|
|
27
|
-
`)}await ae(n,s,r,a,u,!0,!!t)}async function ae(i,t,o,e,n,s,r){let a=N(process.env.HOME||process.env.USERPROFILE||"",".modelweaver");ne(a,{recursive:!0});let u=N(a,"config.yaml");oe(u)&&r?_(`Updating existing config at ${u}`):_(`Writing new config to ${u}`),te(u,e),Ee(i);try{let{readPidFile:c,isProcessAlive:v}=await import("./daemon-27GXX25D.js"),$=await c();if($&&v($)){if(process.platform!=="win32")try{process.kill($,"SIGUSR1")}catch{}else console.log(" Windows does not support SIGUSR1 \u2014 run 'modelweaver reload' to pick up new config.");_("ModelWeaver daemon reloaded with new config")}}catch{}let l=await xe(t);if(l){let c=o.host==="localhost"?`http://localhost:${o.port}`:`http://${o.host}:${o.port}`;V()&&console.log(" Backed up existing settings to settings.json.bak");let $=z(),h=J($,{baseUrl:c,defaultModel:l.defaultModel,availableModels:l.availableModels});Q(h),_(`Claude Code settings updated at ${Y()}`),console.log(` Proxy endpoint: ${c}`),console.log(` Default model: ${l.defaultModel}`),console.log(` Available models: ${l.availableModels.join(", ")}`),console.log(),console.log(` ${ie}Restart Claude Code to apply changes.${d}`)}return l}var B=0,L=1,D=2,ee=3;async function Ge(i){if(i?.quick)return Ae();process.stdin.isTTY||(console.error("Error: modelweaver init requires an interactive terminal."),process.exit(1));let{peekConfig:t}=await import("./config-P34YQCFG.js"),o=t(),e=o?.providers??new Map,n=o?.modelRouting??new Map,s=e.size>0,r=[],a=[],u=[],l={port:3456,host:"localhost"},c,v=null,$=s,h=B;for(;;){if(h<=B){if(F(),console.log(`
|
|
28
|
-
${M}${m}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\u2500
|
|
29
|
-
\u2551 Welcome to ModelWeaver! \u2551
|
|
30
|
-
\u2551 \u2551
|
|
31
|
-
\u2551 This wizard will help you configure \u2551
|
|
32
|
-
\u2551 your multi-provider model proxy. \u2551
|
|
33
|
-
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${d}
|
|
34
|
-
`),s){console.log(`
|
|
35
|
-
${we(e)}
|
|
36
|
-
`);let{action:b}=await p({type:"select",name:"action",message:"What would you like to do?",choices:[{title:"Add new provider(s)",value:"add",description:"Keep existing, add more"},{title:"Edit existing provider",value:"edit",description:"Modify settings of a configured provider"},{title:"Reconfigure all from scratch",value:"reset",description:"Start over (existing providers will be replaced)"}]},g);if(b==="add"){let y=new Set(e.keys()),f=await U({excludeIds:y});f.length===0&&console.log(" No additional providers selected. All existing providers preserved.");let k=x(e.size+f.length,!1);r=[];for(let w=0;w<f.length;w++){let S=await R(f[w],{current:1+w,total:k});S&&r.push(S)}}else if(b==="edit"){let y=[...e.keys()],{editId:f}=await p({type:"select",name:"editId",message:"Select provider to edit:",choices:[{title:"\u2B05 Go back",value:"__back__"},...y.map(P=>{let I=e.get(P);return{title:P,value:P,description:I.baseUrl}})]},g);if(f==="__back__")continue;let k=e.get(f),w=x(e.size,!1),S=await R(f,{current:1,total:w},k);r=S?[S]:[]}else{e.clear(),n.clear(),$=!1;let y=await U(),f=x(y.length,!1);r=[];for(let k=0;k<y.length;k++){let w=await R(y[k],{current:1+k,total:f});w&&r.push(w)}}r.length===0&&e.size===0&&(console.log(`
|
|
37
|
-
${E}No providers configured. Exiting.${d}
|
|
38
|
-
`),process.exit(1))}else{let b=await U(),y=x(b.length,!1);r=[];for(let f=0;f<b.length;f++){let k=await R(b[f],{current:1+f,total:y});k&&r.push(k)}r.length===0&&(console.log(`
|
|
39
|
-
${E}No providers configured. Exiting.${d}
|
|
40
|
-
`),process.exit(1))}u=Pe(r,e),h=L}if(h<=L){a=await _e(u,$?n:void 0);let{nav:b}=await p({type:"select",name:"nav",message:"Next step:",choices:[{title:"Continue to server configuration",value:"next",description:"Configure port and host"},{title:"Go back to provider configuration",value:"back",description:"Reconfigure providers"}]},g);if(b==="back"){h=B;continue}h=D}if(h<=D){console.log();let b=x(u.length,!1),y=u.length+2;l=await Ce({stepInfo:{current:y,total:b}});let{nav:f}=await p({type:"select",name:"nav",message:"Next step:",choices:[{title:"Review and write configuration",value:"next",description:"Confirm and save"},{title:"Go back to model configuration",value:"back",description:"Reconfigure models"}]},g);if(f==="back"){h=L;continue}h=ee}if(h<=ee){c=se(u,a,l,e,n),console.log(`
|
|
41
|
-
${M} Generated configuration:${d}
|
|
42
|
-
`),console.log(re(u,a,l)),console.log(),console.log(c.split(`
|
|
43
|
-
`).map(k=>` ${k}`).join(`
|
|
44
|
-
`));let b=x(u.length,!1),y=u.length+3,{confirm:f}=await p({type:"confirm",name:"confirm",message:`[Step ${y} of ${b}] Write this configuration?`,initial:!0},g);if(f)break;console.log(`
|
|
45
|
-
Returning to server configuration...
|
|
46
|
-
`),h=D;continue}}let A=x(r.length,!1);v=await ae(r,a,l,c,A,!1,s),console.log(`
|
|
47
|
-
${M}${m}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
48
|
-
\u2551 ModelWeaver is configured! \u2551
|
|
49
|
-
\u2551 \u2551
|
|
50
|
-
${v?`\u2551 Claude Code settings have been updated. \u2551
|
|
51
|
-
\u2551 \u2551
|
|
52
|
-
\u2551 Just restart Claude Code to get started. \u2551`:`\u2551 To use with Claude Code: \u2551
|
|
53
|
-
\u2551 \u2551
|
|
54
|
-
\u2551 Terminal 1: \u2551
|
|
55
|
-
\u2551 modelweaver \u2551
|
|
56
|
-
\u2551 \u2551
|
|
57
|
-
\u2551 Terminal 2: \u2551
|
|
58
|
-
\u2551 export ANTHROPIC_BASE_URL=\\ \u2551
|
|
59
|
-
\u2551 http://localhost:${String(l.port).padEnd(25)}\u2551
|
|
60
|
-
\u2551 claude \u2551`}
|
|
61
|
-
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${d}
|
|
62
|
-
`)}export{Ge as runInit,X as testApiKey};
|
|
63
|
-
//# sourceMappingURL=init-VLTKSOZN.js.map
|