@vibeapi/cli 0.0.60 → 0.0.62

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.
@@ -40,6 +40,8 @@ export interface SetupCommandOptions {
40
40
  claudeOfficial?: string;
41
41
  /** Replace each tool's config files wholesale (snapshotted first) instead of merging. */
42
42
  force?: boolean;
43
+ /** Windows: `on` = always put Claude Desktop's config in the registry policy (for when the local file is ignored); `off` = back to automatic. */
44
+ desktopRegistry?: string;
43
45
  }
44
46
  /** `--claude-official on|off` (also true/false, show/hide, 1/0, yes/no); undefined when unrecognized. */
45
47
  export declare function parseOnOff(raw: string | undefined): boolean | undefined;
@@ -1 +1 @@
1
- import e from"chalk";import{getAdapter as o}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{i18n as s}from"../lib/i18n.js";import{configManager as l}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as i}from"../lib/tool-manager.js";import{validateApiKey as n,fetchModels as d}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as r,setCodexOneMillionContext as p}from"../lib/tools/codex.js";import{resolveModelCatalog as c}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as u}from"../lib/tools/claude-desktop.js";async function f(e){const o=await d(e);if(!o.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,probed:!1};const t=o.models.map(e=>e.toLowerCase());return{hasClaude:t.some(e=>e.includes("claude")),hasGpt:t.some(e=>e.startsWith("gpt")||e.includes("gpt-")||/^o\d/.test(e)),hasGrok:t.some(e=>e.includes("grok")),probed:!0}}function m(e,o){return"claude"===o?e.hasClaude:"gpt"===o?e.hasGpt:e.hasGrok}export function parseOnOff(e){const o=(e??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(o)||!["off","false","hide","0","no","n"].includes(o)&&void 0}function g(o){console.log(e.green(o))}function y(o){console.log(e.yellow(o))}function k(o){console.error(e.red(o))}async function _(o,t){console.log(e.gray(s.t("setup.validating",{label:o})));const l=await n(t);return!!l.valid||(k(s.t("setup.invalid_key",{label:o,reason:l.message||l.error||"invalid"})),!1)}async function b(o,t,n,d,p){const c=i[o];try{"codex"===o&&void 0!==n&&l.setToolSetting("codex",r,n),"claude-desktop"===o&&void 0!==d&&l.setToolSetting("claude-desktop",u,!d),await a.applyPrivateConfig(o,t,l.getModelsForTool(o),{force:p})}catch(e){const o=(e instanceof Error?e.message:String(e)).replace(/\s+/g," ").trim();return k("× "+s.t("setup.apply_failed",{tool:c.displayName,reason:o})),!1}return a.printApplied(o),p&&console.log(e.gray(" "+s.t("config.force_done",{tool:c.displayName}))),"codex"===o&&void 0!==n&&console.log(e.gray(" "+s.t(n?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),a.isToolInstalled(o)||console.log(e.yellow(" ! "+s.t("setup.install_hint",{tool:c.displayName,install:c.installCommand}))),!0}export async function setupCommand(e){const n=e.key?.trim(),d=e.claudekey?.trim(),u=e.codexkey?.trim(),h=e.grokkey?.trim(),x=e.desktopkey?.trim(),v=void 0!==e.oneMillion,C=void 0!==e.claudeOfficial;let w;if(C&&(w=parseOnOff(e.claudeOfficial),void 0===w))return k(s.t("setup.claude_official_invalid",{value:String(e.claudeOfficial)})),void(process.exitCode=1);const K=Boolean(n||d||u||h||x);if(!K&&(v||C)){if(v){const o=l.getApiKeyForTool("codex");if(o)try{const t=!0===e.oneMillion;l.setToolSetting("codex",r,t);const a=await c(o);p(t,a??void 0),g(s.t(t?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(e){k(s.t("setup.apply_failed",{tool:i.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}else k(s.t("setup.codex_1m_missing_key")),process.exitCode=1}if(C){const o=l.getApiKeyForTool("claude-desktop");o?await b("claude-desktop",o,void 0,w,e.force)||(process.exitCode=1):(k(s.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!K)return k(s.t("setup.usage")),void(process.exitCode=1);let j=0,G=0;const M=new Set,S=[];d&&S.push({tool:"claude-code",label:"--claudekey",apiKey:d,need:"claude"}),u&&S.push({tool:"codex",label:"--codexkey",apiKey:u,need:"gpt"}),h&&S.push({tool:"grok",label:"--grokkey",apiKey:h,need:"grok"}),x&&S.push({tool:"claude-desktop",label:"--desktopkey",apiKey:x,need:"claude"});for(const o of S){if(!await _(o.label,o.apiKey)){G+=1;continue}const t=await f(o.apiKey);!t.probed||m(t,o.need)?(t.probed||y(s.t("setup.probe_failed",{label:o.label})),l.setToolApiKey(o.tool,o.apiKey),await b(o.tool,o.apiKey,e.oneMillion,w,e.force)?(j+=1,M.add(o.tool)):G+=1):(k(s.t(`setup.key_no_${o.need}`,{label:o.label,tool:i[o.tool].displayName})),G+=1)}if(n){if(!await _("--key",n))return void(process.exitCode=1);l.setApiKey(n);const o=await f(n);if(o.probed||y(s.t("setup.probe_failed",{label:"--key"})),o.probed&&!o.hasClaude&&!o.hasGpt&&!o.hasGrok)return k(s.t("setup.key_no_family")),void(process.exitCode=1);const t=[{tool:"claude-code",capable:o.hasClaude,skipMsg:s.t("setup.skip_no_claude")},{tool:"codex",capable:o.hasGpt,skipMsg:s.t("setup.skip_no_gpt")},{tool:"grok",capable:o.hasGrok,skipMsg:s.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:o.hasClaude&&a.isToolInstalled("claude-desktop"),skipMsg:s.t("setup.skip_desktop_not_installed"),quiet:!o.hasClaude}];for(const o of t)M.has(o.tool)||(o.capable?await b(o.tool,n,e.oneMillion,w,e.force)?(j+=1,M.add(o.tool)):G+=1:o.quiet||y(o.skipMsg))}if(j>0){const e=[...M].filter(e=>!o(e)?.isInstalled).map(e=>i[e]?.command).filter(Boolean).join(" / "),t=s.t("setup.done",{count:String(j)});g("\n"+(e?`${t}${s.t("setup.done_commands",{commands:e})}`:t))}G>0&&k(s.t("setup.failed_summary",{count:String(G)})),await t([...M]),(G>0||0===j)&&(process.exitCode=1)}
1
+ import e from"chalk";import{getAdapter as o}from"../lib/tools/registry.js";import{postApplyPrompts as t}from"../lib/post-apply.js";import{i18n as s}from"../lib/i18n.js";import{configManager as i}from"../lib/config.js";import{toolManager as l,SUPPORTED_TOOLS as a}from"../lib/tool-manager.js";import{validateApiKey as d,fetchModels as n}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as r,setCodexOneMillionContext as p}from"../lib/tools/codex.js";import{resolveModelCatalog as c}from"../lib/model-catalog.js";import{CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING as u,CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING as f}from"../lib/tools/claude-desktop.js";async function g(e){const o=await n(e);if(!o.ok)return{hasClaude:!0,hasGpt:!0,hasGrok:!0,probed:!1};const t=o.models.map(e=>e.toLowerCase());return{hasClaude:t.some(e=>e.includes("claude")),hasGpt:t.some(e=>e.startsWith("gpt")||e.includes("gpt-")||/^o\d/.test(e)),hasGrok:t.some(e=>e.includes("grok")),probed:!0}}function m(e,o){return"claude"===o?e.hasClaude:"gpt"===o?e.hasGpt:e.hasGrok}export function parseOnOff(e){const o=(e??"").trim().toLowerCase();return!!["on","true","show","1","yes","y"].includes(o)||!["off","false","hide","0","no","n"].includes(o)&&void 0}function k(o){console.log(e.green(o))}function y(o){console.log(e.yellow(o))}function _(o){console.error(e.red(o))}async function b(o,t){console.log(e.gray(s.t("setup.validating",{label:o})));const i=await d(t);return!!i.valid||(_(s.t("setup.invalid_key",{label:o,reason:i.message||i.error||"invalid"})),!1)}async function h(o,t,d={}){const{oneMillion:n,officialLogin:p,desktopRegistry:c,force:g}=d,m=a[o];try{"claude-desktop"===o&&void 0!==c&&i.setToolSetting("claude-desktop",u,c),"codex"===o&&void 0!==n&&i.setToolSetting("codex",r,n),"claude-desktop"===o&&void 0!==p&&i.setToolSetting("claude-desktop",f,!p),await l.applyPrivateConfig(o,t,i.getModelsForTool(o),{force:g})}catch(e){const o=(e instanceof Error?e.message:String(e)).replace(/\s+/g," ").trim();return _("× "+s.t("setup.apply_failed",{tool:m.displayName,reason:o})),!1}return l.printApplied(o),g&&console.log(e.gray(" "+s.t("config.force_done",{tool:m.displayName}))),"codex"===o&&void 0!==n&&console.log(e.gray(" "+s.t(n?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))),l.isToolInstalled(o)||console.log(e.yellow(" ! "+s.t("setup.install_hint",{tool:m.displayName,install:m.installCommand}))),!0}export async function setupCommand(e){const d=e.key?.trim(),n=e.claudekey?.trim(),u=e.codexkey?.trim(),f=e.grokkey?.trim(),x=e.desktopkey?.trim(),v=void 0!==e.oneMillion,C=void 0!==e.claudeOfficial;let w;if(C&&(w=parseOnOff(e.claudeOfficial),void 0===w))return _(s.t("setup.claude_official_invalid",{value:String(e.claudeOfficial)})),void(process.exitCode=1);const K=void 0!==e.desktopRegistry;let M;if(K&&(M=parseOnOff(e.desktopRegistry),void 0===M))return _(s.t("setup.desktop_registry_invalid",{value:String(e.desktopRegistry)})),void(process.exitCode=1);const S=C||K,j=Boolean(d||n||u||f||x),G={oneMillion:e.oneMillion,officialLogin:w,desktopRegistry:M,force:e.force};if(!j&&(v||S)){if(v){const o=i.getApiKeyForTool("codex");if(o)try{const t=!0===e.oneMillion;i.setToolSetting("codex",r,t);const l=await c(o);p(t,l??void 0),k(s.t(t?"setup.codex_1m_enabled":"setup.codex_1m_disabled"))}catch(e){_(s.t("setup.apply_failed",{tool:a.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}else _(s.t("setup.codex_1m_missing_key")),process.exitCode=1}if(S){const e=i.getApiKeyForTool("claude-desktop");e?await h("claude-desktop",e,{...G,oneMillion:void 0})?await t(["claude-desktop"]):process.exitCode=1:(_(s.t("setup.desktop_official_login_missing_key")),process.exitCode=1)}return}if(!j)return _(s.t("setup.usage")),void(process.exitCode=1);let T=0,O=0;const A=new Set,N=[];n&&N.push({tool:"claude-code",label:"--claudekey",apiKey:n,need:"claude"}),u&&N.push({tool:"codex",label:"--codexkey",apiKey:u,need:"gpt"}),f&&N.push({tool:"grok",label:"--grokkey",apiKey:f,need:"grok"}),x&&N.push({tool:"claude-desktop",label:"--desktopkey",apiKey:x,need:"claude"});for(const e of N){if(!await b(e.label,e.apiKey)){O+=1;continue}const o=await g(e.apiKey);!o.probed||m(o,e.need)?(o.probed||y(s.t("setup.probe_failed",{label:e.label})),i.setToolApiKey(e.tool,e.apiKey),await h(e.tool,e.apiKey,G)?(T+=1,A.add(e.tool)):O+=1):(_(s.t(`setup.key_no_${e.need}`,{label:e.label,tool:a[e.tool].displayName})),O+=1)}if(d){if(!await b("--key",d))return void(process.exitCode=1);i.setApiKey(d);const e=await g(d);if(e.probed||y(s.t("setup.probe_failed",{label:"--key"})),e.probed&&!e.hasClaude&&!e.hasGpt&&!e.hasGrok)return _(s.t("setup.key_no_family")),void(process.exitCode=1);const o=[{tool:"claude-code",capable:e.hasClaude,skipMsg:s.t("setup.skip_no_claude")},{tool:"codex",capable:e.hasGpt,skipMsg:s.t("setup.skip_no_gpt")},{tool:"grok",capable:e.hasGrok,skipMsg:s.t("setup.skip_no_grok")},{tool:"claude-desktop",capable:e.hasClaude&&l.isToolInstalled("claude-desktop"),skipMsg:s.t("setup.skip_desktop_not_installed"),quiet:!e.hasClaude}];for(const e of o)A.has(e.tool)||(e.capable?await h(e.tool,d,G)?(T+=1,A.add(e.tool)):O+=1:e.quiet||y(e.skipMsg))}if(T>0){const e=[...A].filter(e=>!o(e)?.isInstalled).map(e=>a[e]?.command).filter(Boolean).join(" / "),t=s.t("setup.done",{count:String(T)});k("\n"+(e?`${t}${s.t("setup.done_commands",{commands:e})}`:t))}O>0&&_(s.t("setup.failed_summary",{count:String(O)})),await t([...A]),(O>0||0===T)&&(process.exitCode=1)}
@@ -1 +1 @@
1
- import{Command as e}from"commander";import{i18n as o,detectSystemLang as a}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as i}from"./wizard.js";import{langCommand as n,authCommand as s,doctorCommand as r,configCommand as c,statusCommand as m}from"../commands/index.js";import{logsCommand as l}from"../commands/logs.js";import{completionCommand as p}from"../commands/completion.js";import{benchCommand as d}from"../commands/bench.js";import{updateCommand as g}from"../commands/update.js";import{runModelRefresh as u}from"./model-refresh.js";import{upgradeCommand as f}from"../commands/upgrade.js";import{setupCommand as h}from"../commands/setup.js";import y from"chalk";import{readFileSync as k}from"fs";import{join as b,dirname as v}from"path";import{fileURLToPath as w}from"url";import{setRuntimeOptions as $,detectColorPreferenceFromEnv as C,isColorEnabled as x,isJsonMode as j}from"./runtime-options.js";import{cleanupAnthropicEnvVars as A,machineEnvVarsLeft as I}from"./env-cleanup.js";import{runStartupUpdate as P}from"./auto-update.js";export class Command{program;constructor(){const i=t.isLangChosen()?t.getLang():a();o.loadFromConfig(i),this.program=new e,this.setupProgram()}getVersion(){try{const e=w(import.meta.url),o=v(e),a=b(o,"../../package.json");return JSON.parse(k(a,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(o.t("cli.title")).version(this.getVersion(),"-v, --version",o.t("commands.version")).helpOption("-h, --help",o.t("commands.help")).option("--base <url>","Custom API base URL").option("--non-interactive","Disable interactive prompts (CI mode)").option("--json","Emit machine-readable JSON output where applicable").option("--yes, -y","Auto-confirm prompts that have a default action").option("--dry-run","Show planned changes without writing anywhere").option("--no-color","Disable colored output"),this.program.command("setup").description(o.t("commands.setup")).option("--key <key>","API key for all capable tools (capability auto-detected)").option("--claudekey <key>","Dedicated API key for Claude Code").option("--codexkey <key>","Dedicated API key for Codex").option("--grokkey <key>","Dedicated API key for Grok Build").option("--desktopkey <key>","Dedicated API key for Claude Desktop (3p mode)").option("--1m","Enable the 1M context window for Codex").option("--no-1m","Disable the 1M context window for Codex").option("--claude-official <on|off>","Claude Desktop: show (on) or hide (off, default) the official Claude.ai sign-in entry").option("--force","Replace each tool's config files wholesale (snapshotted first) instead of merging — repairs hand-broken configs").option("--all","Configure every tool the key can serve (default behavior)").action(async e=>{await h({key:e.key,claudekey:e.claudekey,codexkey:e.codexkey,grokkey:e.grokkey,desktopkey:e.desktopkey,all:e.all,oneMillion:e["1m"],claudeOfficial:e.claudeOfficial,force:e.force})}),this.program.command("init").description(o.t("commands.init")).action(async()=>{await this.handleInitCommand()});const e=this.program.command("lang").description(o.t("commands.lang"));e.command("show").description(o.t("lang.show_usage")).action(async()=>{await n(["show"])}),e.command("set <locale>").description(o.t("lang.set_usage")).action(async e=>{await n(["set",e])});const a=this.program.command("auth").description(o.t("commands.auth"));a.argument("[token]","API token").action(async e=>{const o=[];e&&o.push(e),await s(o)}),a.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),a.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async e=>{await s(["reload",e])}),this.program.command("doctor").description(o.t("commands.doctor")).option("--deep","Also send one real Claude Code request through the written config").action(async e=>{await r({deep:e.deep})}),this.program.command("status").description(o.t("commands.status")).action(async()=>{await m()}),this.program.command("logs").description("Inspect vibecli log files").argument("[action]","list|tail|path").argument("[file]","specific log file when using tail").action(async(e,o)=>{const a=[];e&&a.push(e),o&&a.push(o),await l(a)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await p([e])}),this.program.command("update").description("Check for a newer version across CNB and npm mirrors").action(async()=>{await g()}),this.program.command("upgrade [tools...]").description(o.t("commands.upgrade")).action(async(e=[])=>{await f(e)}),this.program.command("bench").description("Benchmark the configured API endpoint").option("--samples <n>","number of samples to send","5").action(async e=>{const o=[];e.samples&&o.push("--samples",e.samples),await d(o)}),this.program.command("config").description("Manage profiles, presets, backups, and per-tool config").argument("[args...]","Subcommand and arguments").allowUnknownOption(!0).action(async e=>{await c(e||[])}),this.program.command("enter [option]").description(o.t("commands.enter")).action(async e=>{if(e)switch(e){case"lang":case"language":await i.configLanguage();break;case"apikey":case"api-key":await i.configApiKey();break;case"models":await i.configModels();break;default:{const o=[e];await c(o);break}}else await i.run()}),this.program.action(async()=>{await i.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:e=>e.name()+" "+e.usage()}),this.program.addHelpText("after",`\n${y.bold(o.t("cli.examples"))}:\n ${y.gray("$ vibecli # Interactive main menu")}\n ${y.gray("$ vibecli init # Run first-time setup wizard")}\n ${y.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${y.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${y.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${y.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${y.gray("$ vibecli config codex --force # Same, for one tool")}\n ${y.gray("$ vibecli status --json # Machine-readable status")}\n ${y.gray("$ vibecli config profile list")}\n ${y.gray("$ vibecli config profile create work")}\n ${y.gray("$ vibecli config profile use work")}\n ${y.gray("$ vibecli config preset save fast")}\n ${y.gray("$ vibecli config backup create snapshot")}\n ${y.gray("$ vibecli auth <token> --non-interactive")}\n ${y.gray("$ vibecli auth revoke --yes")}\n ${y.gray("$ vibecli logs path")}\n ${y.gray("$ vibecli completion bash")}\n ${y.gray("$ vibecli bench --samples 3")}\n ${y.gray("$ vibecli update")}\n ${y.gray("$ vibecli upgrade claude-code")}\n ${y.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=A();if(0===e.length||j())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log(x()?y.yellow(a):a);const t=I();if(t.length>0){const e=o.t("messages.env_vars_machine_left",{vars:t.join(", ")});console.log(x()?y.yellow(e):e)}}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&C();let i;$({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(y.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];if("--base"===a&&e[o+1]){i=e[o+1];break}if(a.startsWith("--base=")){i=a.slice(7);break}}i&&t.setBaseUrl(i)}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await P(e)).handled)return;await u(e),await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=x()?y.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
1
+ import{Command as e}from"commander";import{i18n as o,detectSystemLang as a}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as i}from"./wizard.js";import{langCommand as n,authCommand as s,doctorCommand as r,configCommand as c,statusCommand as m}from"../commands/index.js";import{logsCommand as l}from"../commands/logs.js";import{completionCommand as p}from"../commands/completion.js";import{benchCommand as d}from"../commands/bench.js";import{updateCommand as g}from"../commands/update.js";import{runModelRefresh as u}from"./model-refresh.js";import{upgradeCommand as f}from"../commands/upgrade.js";import{setupCommand as h}from"../commands/setup.js";import y from"chalk";import{readFileSync as k}from"fs";import{join as b,dirname as w}from"path";import{fileURLToPath as v}from"url";import{setRuntimeOptions as $,detectColorPreferenceFromEnv as C,isColorEnabled as x,isJsonMode as j}from"./runtime-options.js";import{cleanupAnthropicEnvVars as A,machineEnvVarsLeft as I}from"./env-cleanup.js";import{runStartupUpdate as P}from"./auto-update.js";export class Command{program;constructor(){const i=t.isLangChosen()?t.getLang():a();o.loadFromConfig(i),this.program=new e,this.setupProgram()}getVersion(){try{const e=v(import.meta.url),o=w(e),a=b(o,"../../package.json");return JSON.parse(k(a,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(o.t("cli.title")).version(this.getVersion(),"-v, --version",o.t("commands.version")).helpOption("-h, --help",o.t("commands.help")).option("--base <url>","Custom API base URL").option("--non-interactive","Disable interactive prompts (CI mode)").option("--json","Emit machine-readable JSON output where applicable").option("--yes, -y","Auto-confirm prompts that have a default action").option("--dry-run","Show planned changes without writing anywhere").option("--no-color","Disable colored output"),this.program.command("setup").description(o.t("commands.setup")).option("--key <key>","API key for all capable tools (capability auto-detected)").option("--claudekey <key>","Dedicated API key for Claude Code").option("--codexkey <key>","Dedicated API key for Codex").option("--grokkey <key>","Dedicated API key for Grok Build").option("--desktopkey <key>","Dedicated API key for Claude Desktop (3p mode)").option("--1m","Enable the 1M context window for Codex").option("--no-1m","Disable the 1M context window for Codex").option("--claude-official <on|off>","Claude Desktop: show (on) or hide (off, default) the official Claude.ai sign-in entry").option("--desktop-registry <on|off>","Windows Claude Desktop: always write the config to the registry policy (on, asks for admin once if needed) or decide automatically (off)").option("--force","Replace each tool's config files wholesale (snapshotted first) instead of merging — repairs hand-broken configs").option("--all","Configure every tool the key can serve (default behavior)").action(async e=>{await h({key:e.key,claudekey:e.claudekey,codexkey:e.codexkey,grokkey:e.grokkey,desktopkey:e.desktopkey,all:e.all,oneMillion:e["1m"],claudeOfficial:e.claudeOfficial,desktopRegistry:e.desktopRegistry,force:e.force})}),this.program.command("init").description(o.t("commands.init")).action(async()=>{await this.handleInitCommand()});const e=this.program.command("lang").description(o.t("commands.lang"));e.command("show").description(o.t("lang.show_usage")).action(async()=>{await n(["show"])}),e.command("set <locale>").description(o.t("lang.set_usage")).action(async e=>{await n(["set",e])});const a=this.program.command("auth").description(o.t("commands.auth"));a.argument("[token]","API token").action(async e=>{const o=[];e&&o.push(e),await s(o)}),a.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),a.command("reload <tool>").description("Reload plan configuration to the specified tool (e.g., claude)").action(async e=>{await s(["reload",e])}),this.program.command("doctor").description(o.t("commands.doctor")).option("--deep","Also send one real Claude Code request through the written config").action(async e=>{await r({deep:e.deep})}),this.program.command("status").description(o.t("commands.status")).action(async()=>{await m()}),this.program.command("logs").description("Inspect vibecli log files").argument("[action]","list|tail|path").argument("[file]","specific log file when using tail").action(async(e,o)=>{const a=[];e&&a.push(e),o&&a.push(o),await l(a)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await p([e])}),this.program.command("update").description("Check for a newer version across CNB and npm mirrors").action(async()=>{await g()}),this.program.command("upgrade [tools...]").description(o.t("commands.upgrade")).action(async(e=[])=>{await f(e)}),this.program.command("bench").description("Benchmark the configured API endpoint").option("--samples <n>","number of samples to send","5").action(async e=>{const o=[];e.samples&&o.push("--samples",e.samples),await d(o)}),this.program.command("config").description("Manage profiles, presets, backups, and per-tool config").argument("[args...]","Subcommand and arguments").allowUnknownOption(!0).action(async e=>{await c(e||[])}),this.program.command("enter [option]").description(o.t("commands.enter")).action(async e=>{if(e)switch(e){case"lang":case"language":await i.configLanguage();break;case"apikey":case"api-key":await i.configApiKey();break;case"models":await i.configModels();break;default:{const o=[e];await c(o);break}}else await i.run()}),this.program.action(async()=>{await i.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:e=>e.name()+" "+e.usage()}),this.program.addHelpText("after",`\n${y.bold(o.t("cli.examples"))}:\n ${y.gray("$ vibecli # Interactive main menu")}\n ${y.gray("$ vibecli init # Run first-time setup wizard")}\n ${y.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${y.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b --grokkey sk-c")}\n ${y.gray("$ vibecli setup --desktopkey sk-d --claude-official off # Claude Desktop, hide Claude.ai login")}\n ${y.gray("$ vibecli setup --key sk-xxx --force # Replace broken tool configs with the recommended ones")}\n ${y.gray("$ vibecli config codex --force # Same, for one tool")}\n ${y.gray("$ vibecli status --json # Machine-readable status")}\n ${y.gray("$ vibecli config profile list")}\n ${y.gray("$ vibecli config profile create work")}\n ${y.gray("$ vibecli config profile use work")}\n ${y.gray("$ vibecli config preset save fast")}\n ${y.gray("$ vibecli config backup create snapshot")}\n ${y.gray("$ vibecli auth <token> --non-interactive")}\n ${y.gray("$ vibecli auth revoke --yes")}\n ${y.gray("$ vibecli logs path")}\n ${y.gray("$ vibecli completion bash")}\n ${y.gray("$ vibecli bench --samples 3")}\n ${y.gray("$ vibecli update")}\n ${y.gray("$ vibecli upgrade claude-code")}\n ${y.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await i.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=A();if(0===e.length||j())return;const a=o.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log(x()?y.yellow(a):a);const t=I();if(t.length>0){const e=o.t("messages.env_vars_machine_left",{vars:t.join(", ")});console.log(x()?y.yellow(e):e)}}preflightArgs(e){const o=o=>e.includes(o),a=!o("--no-color")&&C();let i;$({nonInteractive:o("--non-interactive"),json:o("--json"),yes:o("--yes")||o("-y"),dryRun:o("--dry-run"),color:a}),a||(y.level=0);for(let o=0;o<e.length;o+=1){const a=e[o];if("--base"===a&&e[o+1]){i=e[o+1];break}if(a.startsWith("--base=")){i=a.slice(7);break}}i&&t.setBaseUrl(i)}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await P(e)).handled)return;await u(e),await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const a=x()?y.red:e=>e;console.error(a(o.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
@@ -1 +1 @@
1
- import{existsSync as e,mkdirSync as o,readFileSync as t,writeFileSync as r}from"fs";import{dirname as n,join as s}from"path";import i from"chalk";import{configManager as l}from"./config.js";import{i18n as d}from"./i18n.js";import{resolveModelCatalog as f}from"./model-catalog.js";import{listAdapters as c}from"./tools/registry.js";import{logger as a}from"../utils/logger.js";import{resolveUpdateCachePath as u}from"./auto-update.js";export const MODEL_REFRESH_INTERVAL_MS=36e5;const m=new Set(["setup","init","config","auth","enter","completion","update","lang","logs"]),h=["--json","--dry-run","--help","-h","--version","-v"];export function shouldSkipModelRefresh(e,o=process.env){if("1"===o.VIBECLI_DISABLE_MODEL_REFRESH)return!0;if(h.some(o=>e.includes(o)))return!0;const t=e.find(e=>!e.startsWith("-"));return void 0===t||m.has(t)}export async function refreshModelMenus(){const e=new Map,o=[];for(const t of c())if(t.refreshModelMenu&&t.listedModels)try{const r=l.getApiKeyForTool(t.name);if(!r||t.detectCurrentConfig().apiKey!==r)continue;const n=t.listedModels();if(null===n)continue;e.has(r)||e.set(r,await f(r));const s=e.get(r);if(!s)continue;t.refreshModelMenu({apiKey:r,baseUrl:l.getBaseUrl(),models:l.getModelsForTool(t.name),settings:l.getToolSettings(t.name),catalog:s});const i=t.listedModels()??[],d=i.filter(e=>!n.includes(e)),c=n.filter(e=>!i.includes(e));(d.length>0||c.length>0)&&o.push({tool:t.displayName,added:d,removed:c})}catch(e){a.logError(`modelRefresh(${t.name})`,e)}return o}export async function runModelRefresh(l,f={}){const c=f.env??process.env;if(shouldSkipModelRefresh(l,c))return[];const m=f.now??Date.now(),h=function(e){return s(n(u(e)),"model-refresh.json")}(c);if(m-function(e){try{const o=JSON.parse(t(e,"utf-8")).refreshed_at;return"number"==typeof o?o:0}catch{return 0}}(h)<36e5)return[];let p=[];try{p=await refreshModelMenus(),e(n(h))||o(n(h),{recursive:!0}),r(h,JSON.stringify({refreshed_at:m})+"\n")}catch(e){a.logError("runModelRefresh",e)}const g=f.output??(e=>console.log(i.gray(e)));for(const e of p){const o=[e.added.length>0?d.t("model_refresh.added",{models:e.added.join(", ")}):"",e.removed.length>0?d.t("model_refresh.removed",{models:e.removed.join(", ")}):""].filter(Boolean);g(d.t("model_refresh.changed",{tool:e.tool,changes:o.join(";")}))}return p}
1
+ import{existsSync as e,mkdirSync as o,readFileSync as t,writeFileSync as r}from"fs";import{dirname as n,join as s}from"path";import i from"chalk";import{configManager as l}from"./config.js";import{i18n as f}from"./i18n.js";import{resolveModelCatalog as d}from"./model-catalog.js";import{listAdapters as c}from"./tools/registry.js";import{UserActionNeeded as a}from"./tools/types.js";import{logger as u}from"../utils/logger.js";import{resolveUpdateCachePath as m}from"./auto-update.js";export const MODEL_REFRESH_INTERVAL_MS=36e5;const h=new Set(["setup","init","config","auth","enter","completion","update","lang","logs"]),p=["--json","--dry-run","--help","-h","--version","-v"],g=[];export function shouldSkipModelRefresh(e,o=process.env){if("1"===o.VIBECLI_DISABLE_MODEL_REFRESH)return!0;if(p.some(o=>e.includes(o)))return!0;const t=e.find(e=>!e.startsWith("-"));return void 0===t||h.has(t)}export async function refreshModelMenus(){const e=new Map,o=[];g.length=0;for(const t of c())if(t.refreshModelMenu&&t.listedModels)try{const r=l.getApiKeyForTool(t.name);if(!r||t.detectCurrentConfig().apiKey!==r)continue;const n=t.listedModels();if(null===n)continue;e.has(r)||e.set(r,await d(r));const s=e.get(r);if(!s)continue;t.refreshModelMenu({apiKey:r,baseUrl:l.getBaseUrl(),models:l.getModelsForTool(t.name),settings:l.getToolSettings(t.name),catalog:s});const i=t.listedModels()??[],f=i.filter(e=>!n.includes(e)),c=n.filter(e=>!i.includes(e));(f.length>0||c.length>0)&&o.push({tool:t.displayName,added:f,removed:c})}catch(e){u.logError(`modelRefresh(${t.name})`,e),e instanceof a&&g.push(e.message)}return o}export async function runModelRefresh(l,d={}){const c=d.env??process.env;if(shouldSkipModelRefresh(l,c))return[];const a=d.now??Date.now(),h=function(e){return s(n(m(e)),"model-refresh.json")}(c);if(a-function(e){try{const o=JSON.parse(t(e,"utf-8")).refreshed_at;return"number"==typeof o?o:0}catch{return 0}}(h)<36e5)return[];let p=[];try{p=await refreshModelMenus(),e(n(h))||o(n(h),{recursive:!0}),r(h,JSON.stringify({refreshed_at:a})+"\n")}catch(e){u.logError("runModelRefresh",e)}const M=d.output??(e=>console.log(i.gray(e)));for(const e of g)M(e);for(const e of p){const o=[e.added.length>0?f.t("model_refresh.added",{models:e.added.join(", ")}):"",e.removed.length>0?f.t("model_refresh.removed",{models:e.removed.join(", ")}):""].filter(Boolean);M(f.t("model_refresh.changed",{tool:e.tool,changes:o.join(";")}))}return p}
@@ -1 +1 @@
1
- import o from"chalk";import t from"inquirer";import{i18n as r}from"./i18n.js";import{isNonInteractive as e}from"./runtime-options.js";import{toolManager as i}from"./tool-manager.js";import{getAdapter as n}from"./tools/registry.js";import{upgradeTool as s}from"./tool-upgrade.js";async function a(o){const{ok:r}=await t.prompt([{type:"confirm",name:"ok",message:o,default:!0}]);return r}export async function postApplyPrompts(t){if(e()||!process.stdin.isTTY)return;const p=t.filter(o=>n(o)?.installSpec&&i.outdatedVersion(o));if(p.length>0){const o=p.map(o=>n(o)?.displayName??o).join("、");if(await a(r.t("setup.offer_upgrade",{tools:o})))for(const o of p)await s(o)}for(const e of t){const t=n(e);if(!t?.runningApp?.isRunning())continue;if(!await a(r.t("setup.offer_restart",{tool:t.displayName})))continue;const i=t.runningApp.restart();console.log(i?o.green(" √ "+r.t("setup.restarted",{tool:t.displayName})):o.yellow(" ! "+r.t("setup.restart_failed",{tool:t.displayName})))}}
1
+ import o from"chalk";import t from"inquirer";import{i18n as n}from"./i18n.js";import{isNonInteractive as e,shouldAutoConfirm as i}from"./runtime-options.js";import{toolManager as r}from"./tool-manager.js";import{getAdapter as s}from"./tools/registry.js";import{upgradeTool as a}from"./tool-upgrade.js";async function p(o){const{ok:n}=await t.prompt([{type:"confirm",name:"ok",message:o,default:!0}]);return n}export async function postApplyPrompts(t){const l=!e()&&process.stdin.isTTY;for(const e of t){const t=s(e)?.pendingElevation?.();if(!t)continue;if(!(i()||l&&await p(t.prompt)))continue;const r=s(e)?.displayName??e;console.log(o.gray(" "+n.t("setup.elevation_running"))),console.log(t.run()?o.green(" √ "+n.t("setup.elevation_done",{tool:r})):o.yellow(" ! "+n.t("setup.elevation_failed",{tool:r})))}if(!l)return;const f=t.filter(o=>s(o)?.installSpec&&r.outdatedVersion(o));if(f.length>0){const o=f.map(o=>s(o)?.displayName??o).join("、");if(await p(n.t("setup.offer_upgrade",{tools:o})))for(const o of f)await a(o)}for(const e of t){const t=s(e);if(!t?.runningApp?.isRunning())continue;if(!await p(n.t("setup.offer_restart",{tool:t.displayName})))continue;const i=t.runningApp.restart();console.log(i?o.green(" √ "+n.t("setup.restarted",{tool:t.displayName})):o.yellow(" ! "+n.t("setup.restart_failed",{tool:t.displayName})))}}
@@ -8,6 +8,8 @@ import { type ModelCatalog } from '../model-catalog.js';
8
8
  * the app is gateway-only; `false` leaves the chooser visible.
9
9
  */
10
10
  export declare const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING = "hide_official_login";
11
+ /** Windows: always write the HKCU policy (the user found the local library ignored), never remove it. */
12
+ export declare const CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING = "force_registry";
11
13
  /** One model as advertised to Claude Desktop's picker (`inferenceModels[]`). */
12
14
  export interface InferenceModel {
13
15
  name: string;
@@ -1 +1 @@
1
- import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import{logger as p}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{BRAND_NAME as f}from"../brand.js";import{backupBeforeWrite as m,atomicWriteFile as g}from"./backup.js";import{toolHome as h}from"./types.js";import{canRestartDesktop as y,isDesktopRunning as k,restartDesktop as b,WINDOWS_PACKAGE_FAMILIES as C}from"./desktop-process.js";import{baseModelId as A,isClaudeModel as _}from"../model-catalog.js";const w=f;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";const D=/^[a-f0-9-]{36}$/;function M(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}export function claudeDesktopUserDataDir(){const e=process.env.CLAUDE_USER_DATA_DIR?.trim();if(e)return e;const o=h(),n=Boolean(process.env.VIBECLI_TEST_HOME?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return C.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const E=()=>a(claudeDesktopUserDataDir(),"configLibrary"),S=()=>a(E(),"_meta.json"),v=e=>a(E(),`${e}.json`),x=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function O(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return M(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function P(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),m(n),g(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){p.logError("claude-desktop.writeJson.chmod",e)}}function I(){const e=O(S());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>M(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function L(e){return e?.entries.find(e=>e.name===w&&D.test(e.id))}const j={opus:0,fable:1,sonnet:2,haiku:3};function T(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=T(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function $(e){return T(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>A(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(_)){i=[...new Set(t.models.filter(_).map(A))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=A(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>j[$(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=a(e)-a(t);if(0!==n)return n;const r=T(e),s=T(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const l=new Set,c=new Map;for(const e of n){const t=$(e);t&&i.includes(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=$(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!0),t){o.anthropicFamilyTier=t;const n=c.get(t);n&&n!==e||l.has(t)||(o.isFamilyDefault=!0,l.add(t))}return o})}let N=null,G=null,U=null;function R(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!1!==e.settings?.hide_official_login,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!1}}const F={defaultModelEffort:"medium",modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0};function K(e){return Object.fromEntries(Object.entries(F).filter(([t])=>!e(t)))}const H="HKCU\\SOFTWARE\\Policies\\Claude",B="HKLM\\SOFTWARE\\Policies\\Claude",J="vibecli-claude-policy.reg",V=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let W=V;export function setRegRunnerForTests(e){W=e??V}function z(){return"win32"===process.platform}function Z(e){let t;try{t=W(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function X(){if(null===Z(H))return;const e=a(claudeDesktopUserDataDir(),J);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),m(e),W(["export",H,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}function q(e){X();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];W(["add",H,"/v",t,"/t",e,"/d",n,"/f"])}}const Q=[...Object.keys(R({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(F)];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=d().username}catch{}return[e?`/Library/Managed Preferences/${e}/com.anthropic.claudefordesktop.plist`:"","/Library/Managed Preferences/com.anthropic.claudefordesktop.plist"].filter(Boolean).find(e=>t(e))??null}if("win32"===process.platform)return Z(B)?B:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return p.logError("claude-desktop.detectManagedConfig",e),null}}const Y=/\d+\.\d+\.\d+/;function ee(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const te="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",oe="darwin"===process.platform||"win32"===process.platform?"https://cnb.cool/vibeapi/claude-desktop/-/releases/latest":"https://claude.ai/download",ne="darwin"===process.platform?`open ${oe}`:"win32"===process.platform?`start ${oe}`:`xdg-open ${oe}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:te,installCommand:ne,configPath:S(),isInstalled:function(){const e=h();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),...C.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(h(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?Y.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(h(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return ee(t)}const o=C.map(e=>`'${e}'`).join(",");return ee(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>Y.exec(e)?.[0]??[]))}return null},runningApp:y()?{isRunning:k,restart:b}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const t=I()??{appliedId:"",entries:[]};let o=L(t);o||(o={id:l(),name:w},t.entries.push(o));const n=R(e);G=!0===n.disableDeploymentModeChooser,N={models:n.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const r=O(v(o.id))??{};P(v(o.id),{...K(e=>e in r),...r,...n}),t.appliedId=o.id,P(S(),t);const s=O(x())??{};if("3p"!==s.deploymentMode&&P(x(),{...s,deploymentMode:"3p"}),U=null,z())try{const e=Z(H);q({...K(t=>e?.has(t)??!1),...n})}catch(e){p.logError("claude-desktop.writePolicy",e),U=e instanceof Error?e.message.trim():String(e)}},unloadPrivateConfig(){if(z())try{!function(e){const t=Z(H);if(!t)return;X();let o=t.size;for(const n of e)t.has(n)&&(W(["delete",H,"/v",n,"/f"]),o-=1);o<=0&&W(["delete",H,"/f"])}(Q)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=I(),t=L(e);if(!e||!t)return;m(v(t.id)),s(v(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(m(S()),s(S(),{force:!0})):P(S(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=I(),t=L(e);if(!t||e?.appliedId!==t.id)return null;const o=O(v(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=I(),o=L(t);if(!o||t?.appliedId!==o.id)return;const n=O(v(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog);if(JSON.stringify(n.inferenceModels)!==JSON.stringify(r)&&(P(v(o.id),{...n,inferenceModels:r}),z()&&Z(H)?.has("inferenceModels")))try{q({inferenceModels:r})}catch(e){p.logError("claude-desktop.refreshModelMenu registry",e)}},detectCurrentConfig(){try{if(z()){const e=Z(H)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=L(I());if(!e)return{apiKey:null};const t=O(v(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return p.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=I(),t=L(e);return{meta:e,applied:t?O(v(t.id)):null,desktop:O(x()),...z()?{policy:Object.fromEntries(Z(H)??[])}:{}}}catch(e){return p.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;z()&&Z(H)?.has("inferenceModels")&&(X(),W(["delete",H,"/v","inferenceModels","/f"]),e=!0);const t=L(I()),o=t?O(v(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;P(v(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(N&&N.models.length>0){const{models:t,source:o,groups:n}=N;e.push({kind:"summary",text:u.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:u.t(`tools.claude_desktop.models_written_${o}`,{count:String(t.length),list:t.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", "),groups:n.join(" / ")})})}null!==G&&e.push({kind:"detail",text:u.t(G?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),y()&&!k()||e.push({kind:"action",text:u.t("tools.claude_desktop.restart_hint")}),U?(e.push({kind:"action",text:u.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:U})):z()&&e.push({kind:"detail",text:u.t("tools.claude_desktop.registry_written",{key:H})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:u.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[S(),x()];try{const t=L(I());t&&e.push(v(t.id))}catch{}return e},backupPaths(){const e=[S(),x()];z()&&e.push(a(claudeDesktopUserDataDir(),J));try{const t=L(I());t&&e.push(v(t.id))}catch{}return e}};
1
+ import{chmodSync as e,existsSync as t,mkdirSync as o,readdirSync as n,readFileSync as r,rmSync as s}from"fs";import{dirname as i}from"path";import{join as a}from"path";import{randomUUID as l}from"crypto";import{execFileSync as c}from"child_process";import{userInfo as d}from"os";import{logger as p}from"../../utils/logger.js";import{i18n as u}from"../i18n.js";import{BRAND_NAME as f}from"../brand.js";import{backupBeforeWrite as m,atomicWriteFile as g}from"./backup.js";import{toolHome as h}from"./types.js";import{canRestartDesktop as y,isDesktopRunning as k,restartDesktop as b,WINDOWS_PACKAGE_FAMILIES as _}from"./desktop-process.js";import{setUserRegistryElevated as A}from"./elevated-reg.js";import{UserActionNeeded as C}from"./types.js";import{baseModelId as E,isClaudeModel as D}from"../model-catalog.js";const v=f;export const CLAUDE_DESKTOP_HIDE_OFFICIAL_LOGIN_SETTING="hide_official_login";export const CLAUDE_DESKTOP_FORCE_REGISTRY_SETTING="force_registry";const w=/^[a-f0-9-]{36}$/;function M(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}export function claudeDesktopUserDataDir(){const e=process.env.CLAUDE_USER_DATA_DIR?.trim();if(e)return e;const o=h(),n=Boolean(process.env.VIBECLI_TEST_HOME?.trim());switch(process.platform){case"darwin":return a(o,"Library","Application Support","Claude-3p");case"win32":{const e=!n&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(o,"AppData","Local");return _.map(t=>a(e,"Packages",t,"LocalCache","Local","Claude-3p")).find(e=>t(e))??a(e,"Claude-3p")}default:{const e=!n&&process.env.XDG_CONFIG_HOME?process.env.XDG_CONFIG_HOME:a(o,".config");return a(e,"Claude-3p")}}}const O=()=>a(claudeDesktopUserDataDir(),"configLibrary"),S=()=>a(O(),"_meta.json"),P=e=>a(O(),`${e}.json`),x=()=>a(claudeDesktopUserDataDir(),"claude_desktop_config.json");function I(e){if(!t(e))return null;const o=r(e,"utf-8");if(!o.trim())return null;try{const e=JSON.parse(o);return M(e)?e:null}catch(t){throw new Error(`Existing ${e} cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function L(n,r){const s=i(n);t(s)||o(s,{recursive:!0,mode:448}),m(n),g(n,JSON.stringify(r,null,2)+"\n");try{e(n,384)}catch(e){p.logError("claude-desktop.writeJson.chmod",e)}}function j(){const e=I(S());if(!e)return null;const t=Array.isArray(e.entries)?e.entries.filter(e=>M(e)&&"string"==typeof e.id&&"string"==typeof e.name):[];return{...e,appliedId:"string"==typeof e.appliedId?e.appliedId:"",entries:t}}function T(e){return e?.entries.find(e=>e.name===v&&w.test(e.id))}const $={opus:0,fable:1,sonnet:2,haiku:3};function G(e){let t=/^claude-(opus|sonnet|haiku|fable)-(\d+(?:-\d+)*)/i.exec(e);return t?{family:t[1].toLowerCase(),version:t[2].split("-").map(Number)}:(t=/^claude-(\d+(?:-\d+)*)-(opus|sonnet|haiku)/i.exec(e),t?{family:t[2].toLowerCase(),version:t[1].split("-").map(Number)}:null)}export function humanizeModelId(e){const t=G(e);return t?`${t.family[0].toUpperCase()+t.family.slice(1)} ${t.version.filter(e=>e<1e7).slice(0,2).join(".")}`:e}function N(e){return G(e)?.family}export function buildInferenceModels(e,t){const{primary:o,bySlot:n}=function(e){const t=["opus","sonnet","haiku","reasoning"].map(t=>E(e[t]||"")).filter(Boolean);return{primary:t[0]??"",bySlot:t}}(e),r=new Set,s=t?.labels??{};let i;if(t&&t.models.some(D)){i=[...new Set(t.models.filter(D).map(E))];for(const e of t.oneMillion)r.add(e)}else{i=[];for(const t of["opus","sonnet","haiku","reasoning"]){const o=(e[t]||"").trim(),n=E(o);n&&(i.includes(n)||i.push(n),/\[1m\]/i.test(o)&&r.add(n))}}const a=e=>$[N(e)??""]??9;i.sort((e,t)=>{if(e===o)return-1;if(t===o)return 1;const n=a(e)-a(t);if(0!==n)return n;const r=G(e),s=G(t);return r&&s?function(e,t){const o=Math.max(e.length,t.length);for(let n=0;n<o;n++){const o=(t[n]??0)-(e[n]??0);if(0!==o)return o}return 0}(r.version,s.version):e.localeCompare(t)});const l=new Set,c=new Map;for(const e of n){const t=N(e);t&&i.includes(e)&&!c.has(t)&&c.set(t,e)}return i.map(e=>{const t=N(e),o={name:e,labelOverride:s[e]||humanizeModelId(e)};if(r.has(e)&&(o.supports1m=!0,o.prefer1m=!0),t){o.anthropicFamilyTier=t;const n=c.get(t);n&&n!==e||l.has(t)||(o.isFamilyDefault=!0,l.add(t))}return o})}let R=null,K=null,U=null,F=null;function H(e){return{inferenceProvider:"gateway",inferenceCredentialKind:"static",inferenceGatewayBaseUrl:(e.baseUrl||"").trim().replace(/\/+$/,""),inferenceGatewayApiKey:e.apiKey,inferenceModels:buildInferenceModels(e.models,e.catalog),disableDeploymentModeChooser:!1!==e.settings?.hide_official_login,chatTabEnabled:!0,chatAdvancedFileAnalysisEnabled:!0,autoModeEnabled:!0,coworkEgressAllowedHosts:["*"],disableEssentialTelemetry:!0,disableNonessentialTelemetry:!0,disableNonessentialServices:!1}}const B={defaultModelEffort:"medium",modelPrefer1mContext:!0,skipWebFetchPreflight:!0,toolSearchEnabled:!0,builtinBrowserEnabled:!0,isDesktopExtensionEnabled:!0,sshHostAllowlist:["*"],claudeAiImport:{enabled:!0,automatic3pImport:!0,exportEnabled:!0},coworkVmIpv6Enabled:!0,mcpToolTimeoutSec:3600,updateViaUpdatesHost:!0};function V(e){return Object.fromEntries(Object.entries(B).filter(([t])=>!e(t)))}const J="SOFTWARE\\Policies\\Claude",W=`HKCU\\${J}`,z="HKLM\\SOFTWARE\\Policies\\Claude",Z="vibecli-claude-policy.reg",X=e=>c("reg",e,{stdio:["ignore","pipe","pipe"]}).toString();let q=X;export function setRegRunnerForTests(e){q=e??X}function Y(){return"win32"===process.platform}function Q(e){let t;try{t=q(["query",e])}catch{return null}const o=new Map;for(const e of t.split(/\r?\n/)){const t=/^\s{2,}(\S+)\s+(REG_[A-Z_]+)\s*(.*)$/.exec(e);t&&o.set(t[1],t[3])}return o}function ee(){if(null===Q(W))return;const e=a(claudeDesktopUserDataDir(),Z);try{t(i(e))||o(i(e),{recursive:!0,mode:448}),m(e),q(["export",W,e,"/y"])}catch(e){p.logError("claude-desktop.snapshotPolicy",e)}}function te(e){ee();for(const[t,o]of Object.entries(e)){const[e,n]="boolean"==typeof o?["REG_DWORD",o?"1":"0"]:"number"==typeof o?["REG_DWORD",String(o)]:"string"==typeof o?["REG_SZ",o]:["REG_SZ",JSON.stringify(o)];q(["add",W,"/v",t,"/t",e,"/d",n,"/f"])}}function oe(e){const t=Q(W);if(!t)return;ee();let o=t.size;for(const n of e)t.has(n)&&(q(["delete",W,"/v",n,"/f"]),o-=1);o<=0&&q(["delete",W,"/f"])}const ne=[...Object.keys(H({apiKey:"",baseUrl:"",models:{haiku:"",sonnet:"",opus:"",reasoning:""}})),...Object.keys(B),"banner"];export function detectManagedConfig(){try{if("darwin"===process.platform){let e="";try{e=d().username}catch{}return[e?`/Library/Managed Preferences/${e}/com.anthropic.claudefordesktop.plist`:"","/Library/Managed Preferences/com.anthropic.claudefordesktop.plist"].filter(Boolean).find(e=>t(e))??null}if("win32"===process.platform)return Q(z)?z:null;const e="/etc/claude-desktop/managed-settings.json";return t(e)?e:null}catch(e){return p.logError("claude-desktop.detectManagedConfig",e),null}}const re=/\d+\.\d+\.\d+/;function se(e){const t=e=>e.split(".").map(Number);return e.sort((e,o)=>{const[n,r]=[t(e),t(o)];for(let e=0;e<3;e++)if(n[e]!==r[e])return r[e]-n[e];return 0})[0]??null}const ie="darwin"===process.platform?"open -a Claude":"win32"===process.platform?"Claude":"claude-desktop",ae="darwin"===process.platform||"win32"===process.platform?"https://cnb.cool/vibeapi/claude-desktop/-/releases/latest":"https://claude.ai/download",le="darwin"===process.platform?`open ${ae}`:"win32"===process.platform?`start ${ae}`:`xdg-open ${ae}`;export const claudeDesktopAdapter={name:"claude-desktop",displayName:"Claude Desktop",command:ie,installCommand:le,configPath:S(),isInstalled:function(){const e=h();let o;switch(process.platform){case"darwin":o=["/Applications/Claude.app",a(e,"Applications","Claude.app")];break;case"win32":{const t=process.env.LOCALAPPDATA||a(e,"AppData","Local");o=[a(t,"AnthropicClaude"),a(t,"Programs","Claude"),..._.map(e=>a(t,"Packages",e))];break}default:o=["/usr/lib/claude-desktop","/opt/Claude","/usr/bin/claude-desktop",a(e,".local","bin","claude-desktop")]}return o.push(claudeDesktopUserDataDir(),a(claudeDesktopUserDataDir(),"..","Claude")),o.some(e=>t(e))},installedVersion:function(){if("darwin"===process.platform){for(const e of["/Applications/Claude.app",a(h(),"Applications","Claude.app")]){const o=a(e,"Contents","Info.plist");if(!t(o))continue;const n=/<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(r(o,"utf-8")),s=n?re.exec(n[1])?.[0]:void 0;if(s)return s}return null}if("win32"===process.platform){const e=a(process.env.LOCALAPPDATA||a(h(),"AppData","Local"),"AnthropicClaude");if(t(e)){const t=n(e).map(e=>/^app-(\d+\.\d+\.\d+)$/.exec(e)?.[1]).filter(e=>!!e);if(t.length>0)return se(t)}const o=_.map(e=>`'${e}'`).join(",");return se(c("powershell.exe",["-NoProfile","-Command",`Get-AppxPackage | Where-Object { @(${o}) -contains $_.PackageFamilyName } | ForEach-Object { $_.Version.ToString() }`],{encoding:"utf-8",timeout:15e3,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).split(/\r?\n/).flatMap(e=>re.exec(e)?.[0]??[]))}return null},runningApp:y()?{isRunning:k,restart:b}:void 0,wantsModelCatalog:!0,loadPrivateConfig(e){const o=j()??{appliedId:"",entries:[]};let n=T(o);n||(n={id:l(),name:v},o.entries.push(n));const r=H(e);K=!0===r.disableDeploymentModeChooser,R={models:r.inferenceModels,source:e.catalog?e.catalog.oneMillionSource:"slots",groups:e.catalog?.matchedGroups??[]};const s=I(P(n.id))??{};L(P(n.id),{...V(e=>e in s),...s,...r}),o.appliedId=n.id,L(S(),o);const i=I(x())??{};if("3p"!==i.deploymentMode&&L(x(),{...i,deploymentMode:"3p"}),U=null,F=null,Y()){const o=Q(W),n=ne.filter(e=>o?.has(e)),s={...V(e=>o?.has(e)??!1),...r},i=!0===e.settings?.force_registry,l=!i&&function(){const e=!Boolean(process.env.VIBECLI_TEST_HOME?.trim())&&process.env.LOCALAPPDATA?process.env.LOCALAPPDATA:a(h(),"AppData","Local");return!!t(a(e,"AnthropicClaude"))||_.some(o=>t(a(e,"Packages",o,"LocalCache","Local","Claude-3p")))}();try{l?oe(ne):te(s)}catch(e){p.logError("claude-desktop.policy",e),U=e instanceof Error?e.message.trim():String(e),(n.length>0||i)&&(F=l?{action:"remove",values:Object.fromEntries(n.map(e=>[e,null])),deleteKey:n.length===o.size}:{action:"write",values:s,deleteKey:!1})}}},pendingElevation(){const e=F;return e?{prompt:u.t(`tools.claude_desktop.elevate_${e.action}`),run:()=>{if(!A(J,e.values,e.deleteKey))return!1;const t=Q(W),o="remove"===e.action?Object.keys(e.values).every(e=>!t?.has(e)):Object.keys(e.values).every(e=>t?.has(e));return o&&(F=null,U=null),o}}:null},unloadPrivateConfig(){if(Y())try{oe(ne)}catch(e){p.logError("claude-desktop.removePolicy",e)}const e=j(),t=T(e);if(!e||!t)return;m(P(t.id)),s(P(t.id),{force:!0});const o=e.entries.filter(e=>e.id!==t.id);0===o.length?(m(S()),s(S(),{force:!0})):L(S(),{...e,entries:o,appliedId:e.appliedId===t.id?o[0].id:e.appliedId})},listedModels(){const e=j(),t=T(e);if(!t||e?.appliedId!==t.id)return null;const o=I(P(t.id))?.inferenceModels;return Array.isArray(o)?o.map(e=>String(e.name)):null},refreshModelMenu(e){const t=j(),o=T(t);if(!o||t?.appliedId!==o.id)return;const n=I(P(o.id));if(!n)return;const r=buildInferenceModels(e.models,e.catalog);if(JSON.stringify(n.inferenceModels)!==JSON.stringify(r)){if(Y()&&Q(W)?.has("inferenceModels"))try{te({inferenceModels:r})}catch(e){throw p.logError("claude-desktop.refreshModelMenu registry",e),new C(u.t("tools.claude_desktop.refresh_needs_admin"))}L(P(o.id),{...n,inferenceModels:r})}},detectCurrentConfig(){try{if(Y()){const e=Q(W)?.get("inferenceGatewayApiKey");if(e)return{apiKey:e}}const e=T(j());if(!e)return{apiKey:null};const t=I(P(e.id)),o=t?.inferenceGatewayApiKey;return{apiKey:"string"==typeof o&&o?o:null}}catch(e){return p.logError("claude-desktop.detectCurrentConfig",e),{apiKey:null}}},getConfig(){try{const e=j(),t=T(e);return{meta:e,applied:t?I(P(t.id)):null,desktop:I(x()),...Y()?{policy:Object.fromEntries(Q(W)??[])}:{}}}catch(e){return p.logError("claude-desktop.getConfig",e),null}},clearPinnedModels(){let e=!1;Y()&&Q(W)?.has("inferenceModels")&&(ee(),q(["delete",W,"/v","inferenceModels","/f"]),e=!0);const t=T(j()),o=t?I(P(t.id)):null;if(t&&o&&void 0!==o.inferenceModels){const{inferenceModels:n,...r}=o;L(P(t.id),r),e=!0}return e?["inferenceModels"]:[]},applyNotes(){const e=[];if(R&&R.models.length>0){const{models:t,source:o,groups:n}=R;e.push({kind:"summary",text:u.t("tools.claude_desktop.models_summary",{count:String(t.length),default:t[0].labelOverride??t[0].name})}),e.push({kind:"detail",text:u.t(`tools.claude_desktop.models_written_${o}`,{count:String(t.length),list:t.map(e=>`${e.labelOverride}${e.supports1m?" (1M)":""}`).join(", "),groups:n.join(" / ")})})}null!==K&&e.push({kind:"detail",text:u.t(K?"tools.claude_desktop.official_login_hidden":"tools.claude_desktop.official_login_shown")}),y()&&!k()||e.push({kind:"action",text:u.t("tools.claude_desktop.restart_hint")}),F?(e.push({kind:"action",text:u.t(`tools.claude_desktop.policy_stale_${F.action}`)}),e.push({kind:"detail",text:U??""})):U?(e.push({kind:"detail",text:u.t("tools.claude_desktop.registry_denied")}),e.push({kind:"detail",text:U})):Y()&&e.push({kind:"detail",text:u.t("tools.claude_desktop.registry_written",{key:W})});const t=detectManagedConfig();return t&&e.push({kind:"action",text:u.t("tools.claude_desktop.managed_conflict",{path:t})}),e},forceResetPaths(){const e=[S(),x()];try{const t=T(j());t&&e.push(P(t.id))}catch{}return e},backupPaths(){const e=[S(),x()];Y()&&e.push(a(claudeDesktopUserDataDir(),Z));try{const t=T(j());t&&e.push(P(t.id))}catch{}return e}};
@@ -1 +1 @@
1
- import{existsSync as e,readFileSync as o}from"fs";import{join as t}from"path";import*as n from"@iarna/toml";import{logger as i}from"../../utils/logger.js";import{i18n as r}from"../i18n.js";import{backupBeforeWrite as l,atomicWriteFile as a}from"./backup.js";import{pickModel as s,toolHome as d,withV1 as c}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";import{buildCodexCatalog as u,codexCatalogPath as p,loadBundledCodexModels as g,loadPreviousCodexModels as _,previousGatewayModelIds as f}from"./codex-catalog.js";import{gatewayModelSpecs as x,modelFamily as y}from"../gateway-models.js";const w=()=>t(d(),".codex"),C=()=>t(w(),"config.toml"),h=()=>t(w(),"auth.json"),j="OPENAI_API_KEY",k="custom",b=1e6,E=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function M(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function O(e,o){return M(e[o])||(e[o]={}),e[o]}function v(){if(!e(C()))return{};const t=o(C(),"utf-8");if(!t.trim())return{};try{const e=n.parse(t);if(!M(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function N(){if(!e(h()))return{};try{const e=JSON.parse(o(h(),"utf-8"));return M(e)?e:{}}catch{return{}}}let S=null;function A(t,n,r){const s=!1!==n.settings?.one_million_context;!function(e){e.model_context_window===b&&delete e.model_context_window,e.model_auto_compact_token_limit===E&&delete e.model_auto_compact_token_limit}(t);const d=g()??_();if(!d)return i.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),S="skipped",t.model_catalog_json===p()&&delete t.model_catalog_json,void(s&&(t.model_context_window=b,t.model_auto_compact_token_limit=E));const c=n.catalog,m=u({bundled:d,specs:x("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??f(),oneMillion:s,selectedModel:r,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});S={listed:m.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))};const y=JSON.stringify(m,null,2)+"\n",w=p();e(w)&&o(w,"utf-8")===y||(l(w),a(w,y)),t.model_catalog_json=w}export function setCodexOneMillionContext(e,o){const t=v(),i=n.stringify(t),r="string"==typeof t.model?t.model:m("codex");A(t,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:o},r);const s=n.stringify(t);s!==i&&(l(C()),a(C(),s))}export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:"npm install -g @openai/codex",upgradeCommand:"npm install -g @openai/codex@latest",installSpec:{command:"codex",npmPackage:"@openai/codex",brew:"codex"},configPath:C(),wantsModelCatalog:!0,listedModels(){if(v().model_catalog_json!==p())return null;try{return(JSON.parse(o(p(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){v().model_catalog_json===p()&&setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog)},applyNotes(){if("skipped"===S)return[{kind:"action",text:r.t("tools.codex.catalog_skipped")}];if(!S)return[];const e=new Map;for(const o of S.listed)e.set(y(o.id),(e.get(y(o.id))??0)+1);const o=["gpt","claude","other","grok"].filter(o=>e.has(o)).map(o=>`${r.t(`tools.model_family.${o}`)} ${e.get(o)}`).join(" · ");return[{kind:"summary",text:r.t("tools.codex.catalog_written",{count:String(S.listed.length),breakdown:o})},{kind:"detail",text:S.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const o=m("codex"),t=s(e.models,o),i=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(t)?o:t,r=c(e.baseUrl),d=v();d.model=i,d.model_provider=k,d.model_reasoning_effort="xhigh",A(d,e,i);const u=O(d,"model_providers"),p=M(u[k])?{...u[k]}:{};delete p.env_key,u[k]={...p,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r};const g=O(d,"profiles"),_=M(g[i])?{...g[i]}:{};g[i]={..._,model_provider:k,model:i},O(O(d,"tui"),"model_availability_nux")[i]=4;const f=O(d,"features");f.goals=!0,f.js_repl=!1,l(C()),a(C(),n.stringify(d)),function(e){const o=e.trim();if(!o)return;const t={...N(),auth_mode:"apikey",[j]:o};l(h()),a(h(),JSON.stringify(t,null,2)+"\n")}(e.apiKey)},unloadPrivateConfig(){if(!e(C()))return;let o;try{o=v()}catch(e){return void i.logError("codexAdapter.unloadPrivateConfig",e)}const t=o.model_providers;M(t)&&(delete t[k],0===Object.keys(t).length&&delete o.model_providers),o.model_provider===k&&delete o.model_provider,o.model_catalog_json===p()&&delete o.model_catalog_json,l(C()),a(C(),n.stringify(o))},detectCurrentConfig(){const e=N()[j];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return v()}catch(e){return i.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[C(),h(),p()]};
1
+ import{existsSync as e,readFileSync as o}from"fs";import{join as t}from"path";import*as n from"@iarna/toml";import{logger as i}from"../../utils/logger.js";import{i18n as r}from"../i18n.js";import{backupBeforeWrite as l,atomicWriteFile as a}from"./backup.js";import{pickModel as s,toolHome as d,withV1 as c}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";import{buildCodexCatalog as u,codexCatalogPath as p,loadBundledCodexModels as g,loadPreviousCodexModels as _,previousGatewayModelIds as f}from"./codex-catalog.js";import{gatewayModelSpecs as x,modelFamily as y}from"../gateway-models.js";const w=()=>t(d(),".codex"),C=()=>t(w(),"config.toml"),h=()=>t(w(),"auth.json"),j="OPENAI_API_KEY",k="custom",b=1e6,E=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function M(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}function O(e,o){return M(e[o])||(e[o]={}),e[o]}function v(){if(!e(C()))return{};const t=o(C(),"utf-8");if(!t.trim())return{};try{const e=n.parse(t);if(!M(e))throw new Error("config.toml root must be a TOML table");return e}catch(e){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${e instanceof Error?e.message:String(e)}`)}}function N(){if(!e(h()))return{};try{const e=JSON.parse(o(h(),"utf-8"));return M(e)?e:{}}catch{return{}}}let S=null;function A(t,n,r){const s=!1!==n.settings?.one_million_context;!function(e){e.model_context_window===b&&delete e.model_context_window,e.model_auto_compact_token_limit===E&&delete e.model_auto_compact_token_limit}(t);const d=g()??_();if(!d)return i.logError("codexAdapter.writeModelCatalog",new Error("no Codex binary found; model catalog skipped")),S="skipped",t.model_catalog_json===p()&&delete t.model_catalog_json,void(s&&(t.model_context_window=b,t.model_auto_compact_token_limit=E));const c=n.catalog,m=u({bundled:d,specs:x("codex",c?.clientModels),...c?{keyModels:c.models}:{},gatewayAvailable:c?.models??f(),oneMillion:s,selectedModel:r,..."gateway"===c?.oneMillionSource?{claudeOneMillion:c.oneMillion}:{}});S={listed:m.models.filter(e=>"list"===e.visibility).map(e=>({id:String(e.slug),name:String(e.display_name??e.slug)}))};const y=JSON.stringify(m,null,2)+"\n",w=p();e(w)&&o(w,"utf-8")===y||(l(w),a(w,y)),t.model_catalog_json=w}export function setCodexOneMillionContext(e,o){const t=v(),i=n.stringify(t),r="string"==typeof t.model?t.model:m("codex");A(t,{settings:{[CODEX_ONE_MILLION_CONTEXT_SETTING]:e},catalog:o},r);const s=n.stringify(t);s!==i&&(l(C()),a(C(),s))}export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:"npm install -g @openai/codex",upgradeCommand:"npm install -g @openai/codex@latest",installSpec:{command:"codex",npmPackage:"@openai/codex",brew:"codex"},configPath:C(),wantsModelCatalog:!0,listedModels(){if(v().model_catalog_json!==p())return null;try{return(JSON.parse(o(p(),"utf-8")).models??[]).filter(e=>"list"===e.visibility).map(e=>String(e.slug))}catch{return null}},refreshModelMenu(e){v().model_catalog_json===p()&&setCodexOneMillionContext(!1!==e.settings?.one_million_context,e.catalog)},applyNotes(){if("skipped"===S)return[{kind:"action",text:r.t("tools.codex.catalog_skipped")}];if(!S)return[];const e=new Map;for(const o of S.listed)e.set(y(o.id),(e.get(y(o.id))??0)+1);const o=["gpt","claude","other","grok"].filter(o=>e.has(o)).map(o=>`${r.t(`tools.model_family.${o}`)} ${e.get(o)}`).join(" · ");return[{kind:"summary",text:r.t("tools.codex.catalog_written",{count:String(S.listed.length),breakdown:o})},{kind:"detail",text:S.listed.map(e=>e.name).join(", ")}]},loadPrivateConfig(e){const o=m("codex"),t=s(e.models,o),i=function(e){return/(?:^|\/)claude(?:-|$)/i.test(e)}(t)?o:t,r=c(e.baseUrl),d=v();d.model=i,d.model_provider=k,d.model_reasoning_effort="high",A(d,e,i);const u=O(d,"model_providers"),p=M(u[k])?{...u[k]}:{};delete p.env_key,u[k]={...p,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:r};const g=O(d,"profiles"),_=M(g[i])?{...g[i]}:{};g[i]={..._,model_provider:k,model:i},O(O(d,"tui"),"model_availability_nux")[i]=4;const f=O(d,"features");f.goals=!0,f.js_repl=!1,l(C()),a(C(),n.stringify(d)),function(e){const o=e.trim();if(!o)return;const t={...N(),auth_mode:"apikey",[j]:o};l(h()),a(h(),JSON.stringify(t,null,2)+"\n")}(e.apiKey)},unloadPrivateConfig(){if(!e(C()))return;let o;try{o=v()}catch(e){return void i.logError("codexAdapter.unloadPrivateConfig",e)}const t=o.model_providers;M(t)&&(delete t[k],0===Object.keys(t).length&&delete o.model_providers),o.model_provider===k&&delete o.model_provider,o.model_catalog_json===p()&&delete o.model_catalog_json,l(C()),a(C(),n.stringify(o))},detectCurrentConfig(){const e=N()[j];return{apiKey:"string"==typeof e&&e.trim()?e.trim():null}},getConfig(){try{return v()}catch(e){return i.logError("codexAdapter.getConfig",e),null}},backupPaths:()=>[C(),h(),p()]};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Change registry values that need an administrator (`HKCU\Software\Policies` is
3
+ * read-only for standard users) through one UAC prompt: a `.reg` file imported by
4
+ * an elevated `reg.exe` — what double-clicking the gateway's downloadable `.reg`
5
+ * does too. The key is addressed as `HKEY_USERS\<SID>` rather than HKCU, so it
6
+ * still lands in this user's hive when the prompt is answered with another
7
+ * (administrator) account.
8
+ */
9
+ /** A value to write; `null` deletes the value. */
10
+ export type RegValue = string | number | boolean | object | null;
11
+ /**
12
+ * `.reg` text for one key under `HKEY_USERS\<sid>\<subKey>`. `deleteKey` removes
13
+ * the whole key instead (used when every value in it is ours).
14
+ */
15
+ export declare function buildRegFile(sid: string, subKey: string, values: Record<string, RegValue>, deleteKey?: boolean): string;
16
+ /** Tests replace the SID lookup and the elevated import. */
17
+ export declare function setElevationForTests(sid: () => string | null, runner: (regFile: string) => boolean): void;
18
+ /** Write / delete values under HKCU-equivalent `subKey` with one UAC prompt. */
19
+ export declare function setUserRegistryElevated(subKey: string, values: Record<string, RegValue>, deleteKey?: boolean): boolean;
@@ -0,0 +1 @@
1
+ import{spawnSync as r}from"child_process";import{mkdtempSync as e,rmSync as t,writeFileSync as o}from"fs";import{tmpdir as n}from"os";import{join as i}from"path";import{logger as s}from"../../utils/logger.js";function u(r){return`"${r.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}export function buildRegFile(r,e,t,o=!1){const n=`HKEY_USERS\\${r}\\${e}`;return o?`Windows Registry Editor Version 5.00\r\n\r\n[-${n}]\r\n`:`Windows Registry Editor Version 5.00\r\n\r\n[${n}]\r\n${Object.entries(t).map(([r,e])=>`${u(r)}=${null===e?"-":function(r){return"boolean"==typeof r?"dword:"+(r?"00000001":"00000000"):"number"==typeof r?`dword:${(r>>>0).toString(16).padStart(8,"0")}`:u("string"==typeof r?r:JSON.stringify(r))}(e)}`).join("\r\n")}\r\n`}let f=()=>{const e=r("whoami",["/user","/fo","csv","/nh"],{encoding:"utf-8",windowsHide:!0});return/"(S-1-[\d-]+)"/.exec(`${e.stdout??""}`)?.[1]??null},l=e=>{const t=r("powershell.exe",["-NoProfile","-NonInteractive","-Command",`$p = Start-Process -FilePath 'reg.exe' -ArgumentList @('import', '"${e}"') -Verb RunAs -Wait -PassThru -WindowStyle Hidden; exit $p.ExitCode`],{encoding:"utf-8",windowsHide:!0,timeout:3e5});return 0!==t.status&&s.logError("elevatedImport",new Error(`${t.stderr??""}`.trim()||`exit ${t.status}`)),0===t.status};export function setElevationForTests(r,e){f=r,l=e}export function setUserRegistryElevated(r,s,u=!1){const d=f();if(!d)return!1;const c=e(i(n(),"vibecli-reg-")),a=i(c,"vibecli.reg");try{return o(a,Buffer.concat([Buffer.from([255,254]),Buffer.from(buildRegFile(d,r,s,u),"utf16le")])),l(a)}finally{t(c,{recursive:!0,force:!0})}}
@@ -66,6 +66,14 @@ export interface ToolAdapter {
66
66
  refreshModelMenu?(ctx: ToolApplyContext & {
67
67
  catalog: ModelCatalog;
68
68
  }): void;
69
+ /**
70
+ * A change the last apply could not make without an administrator (one UAC
71
+ * prompt fixes it); setup and the wizard offer to run it. Null when none.
72
+ */
73
+ pendingElevation?(): {
74
+ prompt: string;
75
+ run(): boolean;
76
+ } | null;
69
77
  /**
70
78
  * GUI apps that only read their config at start: setup and the wizard offer
71
79
  * to restart a running app after writing it.
@@ -149,3 +157,7 @@ export declare function pickModel(models: VibecliModels, fallback: string): stri
149
157
  * raw base URL instead. Adjust here if a gateway uses a different path.
150
158
  */
151
159
  export declare function withV1(baseUrl: string): string;
160
+ /** Thrown by a background refresh that needs the user to do something; its message is shown as is. */
161
+ export declare class UserActionNeeded extends Error {
162
+ constructor(message: string);
163
+ }
@@ -1 +1 @@
1
- import{homedir as o}from"os";import{getDefaultModels as r}from"../model-registry.js";export function toolHome(){const r=process.env.VIBECLI_TEST_HOME;return r&&r.trim()?r.trim():o()}export function primaryModel(o){return o.opus||o.sonnet||o.reasoning||o.haiku}export function pickModel(o,t){const e=primaryModel(o);return e&&e!==r().opus?e:t}export function withV1(o){const r=(o||"").replace(/\/+$/,"");return/\/v\d+$/.test(r)?r:`${r}/v1`}
1
+ import{homedir as r}from"os";import{getDefaultModels as e}from"../model-registry.js";export function toolHome(){const e=process.env.VIBECLI_TEST_HOME;return e&&e.trim()?e.trim():r()}export function primaryModel(r){return r.opus||r.sonnet||r.reasoning||r.haiku}export function pickModel(r,o){const t=primaryModel(r);return t&&t!==e().opus?t:o}export function withV1(r){const e=(r||"").replace(/\/+$/,"");return/\/v\d+$/.test(e)?e:`${e}/v1`}export class UserActionNeeded extends Error{constructor(r){super(r),this.name="UserActionNeeded"}}
@@ -354,7 +354,11 @@
354
354
  "offer_upgrade": "{{tools}} is too old; upgrade now?",
355
355
  "offer_restart": "{{tool}} is running; restart it so the new config takes effect? (closes its windows)",
356
356
  "restarted": "{{tool}} restarted",
357
- "restart_failed": "Could not restart {{tool}}; quit it completely and open it again"
357
+ "restart_failed": "Could not restart {{tool}}; quit it completely and open it again",
358
+ "elevation_running": "Click Yes in the Windows prompt…",
359
+ "elevation_done": "{{tool}} registry updated",
360
+ "elevation_failed": "Could not update {{tool}}'s registry (prompt declined, or this account is not an administrator): open PowerShell as administrator and run vibecli setup",
361
+ "desktop_registry_invalid": "--desktop-registry takes on or off, got {{value}}"
358
362
  },
359
363
  "tools": {
360
364
  "version_outdated": "version {{current}} is too old (needs {{min}} or later); upgrade with {{upgrade}}",
@@ -372,7 +376,12 @@
372
376
  "official_login_hidden": "The official Claude.ai sign-in entry is hidden; the desktop is gateway-only. Run vibecli setup --claude-official on to bring it back",
373
377
  "official_login_shown": "The official Claude.ai sign-in entry stays visible, so users can switch between the gateway and their own account. Run vibecli setup --claude-official off to hide it",
374
378
  "models_summary": "{{count}} Claude models, default {{default}}",
375
- "registry_denied": "Not elevated, so the registry backstop was skipped (the local config is written and usually enough); if the reopened app doesn't use VibeAPI, run this again from an administrator terminal"
379
+ "registry_denied": "No administrator rights, registry backstop skipped; there are no old registry settings, so the local config applies",
380
+ "policy_stale_remove": "Claude Desktop still uses old registry settings (most likely from the .reg file downloaded from the gateway page), which override the new config; removing them needs one administrator confirmation, after which updates need none",
381
+ "policy_stale_write": "Claude Desktop still uses old registry settings (most likely from the .reg file downloaded from the gateway page); writing the new config there needs one administrator confirmation",
382
+ "elevate_remove": "Remove the old registry settings? Windows asks for administrator confirmation once; later updates won't need it",
383
+ "elevate_write": "Write the new config to the registry? Windows asks for administrator confirmation once",
384
+ "refresh_needs_admin": "Claude Desktop's model list lives in the registry and needs administrator confirmation to change: run vibecli setup and confirm"
376
385
  },
377
386
  "claude_code": {
378
387
  "gateway_models": "{{count}} more models added to /model ({{vendors}})",
@@ -354,7 +354,11 @@
354
354
  "offer_upgrade": "{{tools}} 版本过旧,现在升级吗?",
355
355
  "offer_restart": "{{tool}} 正在运行,要重启它让新配置生效吗?(会关掉当前窗口)",
356
356
  "restarted": "{{tool}} 已重启",
357
- "restart_failed": "{{tool}} 没能自动重启,请完全退出后手动打开"
357
+ "restart_failed": "{{tool}} 没能自动重启,请完全退出后手动打开",
358
+ "elevation_running": "请在弹出的系统窗口里点「是」…",
359
+ "elevation_done": "{{tool}} 的注册表已更新",
360
+ "elevation_failed": "没能更新 {{tool}} 的注册表(系统确认被取消,或这个账号不是管理员):用管理员身份打开 PowerShell 再运行 vibecli setup",
361
+ "desktop_registry_invalid": "--desktop-registry 只接受 on 或 off,收到的是 {{value}}"
358
362
  },
359
363
  "tools": {
360
364
  "version_outdated": "版本 {{current}} 过旧,需要 {{min}} 及以上,运行 {{upgrade}} 升级",
@@ -372,7 +376,12 @@
372
376
  "official_login_hidden": "官方登录(Claude.ai 账号)入口已隐藏,桌面端只走网关;要恢复运行 vibecli setup --claude-official on",
373
377
  "official_login_shown": "官方登录(Claude.ai 账号)入口保持可见,用户可在网关与官方账号间切换;要隐藏运行 vibecli setup --claude-official off",
374
378
  "models_summary": "{{count}} 个 Claude 模型,默认 {{default}}",
375
- "registry_denied": "没有管理员权限,跳过了注册表兜底(本地配置已写好,通常不影响);如果重开桌面端后没有走 VibeAPI,用管理员身份打开终端再运行一次"
379
+ "registry_denied": "没有管理员权限,跳过了注册表兜底;注册表里没有旧配置,本地配置会生效",
380
+ "policy_stale_remove": "桌面端还在用注册表里的旧配置(多半是以前从网关页面下载 .reg 写入的),它会挡住刚写好的配置;删掉它需要管理员确认一次,之后更新不再需要管理员",
381
+ "policy_stale_write": "桌面端还在用注册表里的旧配置(多半是以前从网关页面下载 .reg 写入的);把新配置写进去需要管理员确认一次",
382
+ "elevate_remove": "删掉注册表里的旧配置吗?会弹出一次系统的管理员确认,之后更新桌面端不再需要管理员",
383
+ "elevate_write": "把新配置写进注册表吗?会弹出一次系统的管理员确认",
384
+ "refresh_needs_admin": "Claude 桌面端的模型列表在注册表里,更新它需要管理员确认:运行 vibecli setup 按提示确认即可"
376
385
  },
377
386
  "claude_code": {
378
387
  "gateway_models": "/model 菜单加入 {{count}} 个其他模型({{vendors}})",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/cli",
3
- "version": "0.0.60",
3
+ "version": "0.0.62",
4
4
  "description": "CLI tool for managing AI coding assistants (Claude Code, Codex, OpenClaw, Hermes, AtomCode, Grok Build) with one unified config",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",