@vibeapi/cli 0.0.36 → 0.0.38
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/README.md +9 -1
- package/dist/commands/setup.d.ts +4 -0
- package/dist/commands/setup.js +1 -1
- package/dist/lib/claude-code-manager.js +1 -1
- package/dist/lib/command.js +1 -1
- package/dist/lib/config.d.ts +8 -0
- package/dist/lib/config.js +1 -1
- package/dist/lib/tool-manager.js +1 -1
- package/dist/lib/tools/codex.d.ts +3 -0
- package/dist/lib/tools/codex.js +1 -1
- package/dist/lib/tools/types.d.ts +2 -1
- package/dist/lib/wizard.js +1 -1
- package/dist/locales/en_US.json +4 -1
- package/dist/locales/zh_CN.json +4 -1
- package/dist/settings-template.json +2 -1
- package/package.json +2 -2
- package/settings-template.json +2 -1
package/README.md
CHANGED
|
@@ -42,6 +42,13 @@ vibecli setup --key sk-xxx
|
|
|
42
42
|
vibecli setup --claudekey sk-a --codexkey sk-b
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
Codex 同时开启 1M 上下文时,末尾加最短参数 `--1m`;已经配置过的用户无需再次输入 Key。单独执行 `--1m` 只幂等更新上下文字段,不改写 Codex 凭据:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
vibecli setup --codexkey sk-xxx --1m
|
|
49
|
+
vibecli setup --1m
|
|
50
|
+
```
|
|
51
|
+
|
|
45
52
|
配完直接运行 `claude` / `codex` 即可。
|
|
46
53
|
|
|
47
54
|
### 方式 B:交互式向导
|
|
@@ -70,7 +77,8 @@ vibecli config claude-code clear-model # 模型选择被钉住时,交还给
|
|
|
70
77
|
|
|
71
78
|
- **多工具一键配置**:把当前 API Key / 模型注入 Claude Code、Codex、OpenClaw、Hermes、AtomCode(OpenCode 暂时下架,`vibecli config opencode` 仍可用)。
|
|
72
79
|
- **按工具选模型**:使用默认 / 实时从 API 拉取 / 内置预设 / 手动输入;每个工具可用不同模型。
|
|
73
|
-
- Claude Code
|
|
80
|
+
- Claude Code 用默认模型时不写 `ANTHROPIC_*_MODEL` 环境变量,顶层 `model` 由 `settings-template.json` 提供(当前为 `opus[1m]`);显式选择其它模型时会覆盖该模板值。Codex 默认 `gpt-5.6-sol`,其余工具默认 `gpt-5.5`。
|
|
81
|
+
- Codex 默认启用 1M 上下文窗口(自动压缩阈值 900K);交互配置 Codex 时可取消。
|
|
74
82
|
- **不动你的其它配置**:只改本工具的 provider 字段,你的 hooks、MCP、插件、权限设置原样保留。
|
|
75
83
|
- **改前自动备份 + 一键还原**:覆盖配置前先快照(每文件留 10 份),`vibecli config <tool> restore` 随时回退;还原本身也可再撤销。
|
|
76
84
|
- **多 Profile + 可迁移凭据**:work/personal 各自独立;API Key 存在权限为 `0600` 的 `~/.vibecli/config.yaml`,与 Claude/Codex 本身的明文凭据方式一致,换机不会被主机绑定加密阻断。
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
* 双令牌各配各的(如 Claude 分组 + GPT 分组),各自校验 + 能力核对,
|
|
10
10
|
* 明文存入 config.yaml 的工具专属 Key。只给其中一个旗标就只配对应工具。
|
|
11
11
|
*
|
|
12
|
+
* vibecli setup --1m
|
|
13
|
+
* 复用当前 profile 已保存的 Codex Key,开启 1M 上下文并重新应用配置。
|
|
14
|
+
*
|
|
12
15
|
* 重复执行即换令牌(幂等)。模型一律用默认组合,之后可在向导 / tool-model 里改。
|
|
13
16
|
*/
|
|
14
17
|
export interface SetupCommandOptions {
|
|
@@ -16,5 +19,6 @@ export interface SetupCommandOptions {
|
|
|
16
19
|
claudekey?: string;
|
|
17
20
|
codexkey?: string;
|
|
18
21
|
all?: boolean;
|
|
22
|
+
oneMillion?: boolean;
|
|
19
23
|
}
|
|
20
24
|
export declare function setupCommand(options: SetupCommandOptions): Promise<void>;
|
package/dist/commands/setup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"chalk";import{i18n as o}from"../lib/i18n.js";import{configManager as t}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as
|
|
1
|
+
import e from"chalk";import{i18n as o}from"../lib/i18n.js";import{configManager as t}from"../lib/config.js";import{toolManager as a,SUPPORTED_TOOLS as s}from"../lib/tool-manager.js";import{validateApiKey as l,fetchModels as i}from"../lib/api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as n,setCodexOneMillionContext as d}from"../lib/tools/codex.js";async function r(e){const o=await i(e);if(!o.ok)return{hasClaude:!0,hasGpt:!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)),probed:!0}}function p(o){console.log(e.green(o))}function c(o){console.log(e.yellow(o))}function u(o){console.error(e.red(o))}async function m(t,a){console.log(e.gray(o.t("setup.validating",{label:t})));const s=await l(a);return!!s.valid||(u(o.t("setup.invalid_key",{label:t,reason:s.message||s.error||"invalid"})),!1)}function f(l,i,d=!1){const r=s[l];try{"codex"===l&&d&&t.setToolSetting("codex",n,!0),a.loadPrivateConfig(l,i,t.getModelsForTool(l))}catch(e){return u(o.t("setup.apply_failed",{tool:r.displayName,reason:e instanceof Error?e.message:String(e)})),!1}return p(o.t("setup.configured",{tool:r.displayName,command:r.command})),"codex"===l&&d&&p(o.t("setup.codex_1m_enabled")),a.isToolInstalled(l)||console.log(e.gray(o.t("setup.install_hint",{tool:r.displayName,install:r.installCommand}))),!0}export async function setupCommand(e){const a=e.key?.trim(),l=e.claudekey?.trim(),i=e.codexkey?.trim(),y=!0===e.oneMillion;if(!a&&!l&&!i&&y){if(!t.getApiKeyForTool("codex"))return u(o.t("setup.codex_1m_missing_key")),void(process.exitCode=1);try{t.setToolSetting("codex",n,!0),d(!0),p(o.t("setup.codex_1m_enabled"))}catch(e){u(o.t("setup.apply_failed",{tool:s.codex.displayName,reason:e instanceof Error?e.message:String(e)})),process.exitCode=1}return}if(!a&&!l&&!i)return u(o.t("setup.usage")),void(process.exitCode=1);let g=0,b=0;const _=new Set,x=[];l&&x.push({tool:"claude-code",label:"--claudekey",apiKey:l,need:"claude"}),i&&x.push({tool:"codex",label:"--codexkey",apiKey:i,need:"gpt"});for(const e of x){if(!await m(e.label,e.apiKey)){b+=1;continue}const a=await r(e.apiKey),s="claude"===e.need?a.hasClaude:a.hasGpt;!a.probed||s?(a.probed||c(o.t("setup.probe_failed",{label:e.label})),t.setToolApiKey(e.tool,e.apiKey),f(e.tool,e.apiKey,y)?(g+=1,_.add(e.tool)):b+=1):(u(o.t("claude"===e.need?"setup.key_no_claude":"setup.key_no_gpt",{label:e.label})),b+=1)}if(a){if(!await m("--key",a))return void(process.exitCode=1);t.setApiKey(a);const e=await r(a);if(e.probed||c(o.t("setup.probe_failed",{label:"--key"})),e.probed&&!e.hasClaude&&!e.hasGpt)return u(o.t("setup.key_no_family")),void(process.exitCode=1);const s=[{tool:"claude-code",capable:e.hasClaude,skipMsg:o.t("setup.skip_no_claude")},{tool:"codex",capable:e.hasGpt,skipMsg:o.t("setup.skip_no_gpt")}];for(const e of s)_.has(e.tool)||(e.capable?f(e.tool,a,y)?(g+=1,_.add(e.tool)):b+=1:c(e.skipMsg))}if(g>0){const e=[..._].map(e=>s[e]?.command).filter(Boolean).join(" / ");p("\n"+o.t("setup.done",{count:String(g),commands:e}))}(b>0||0===g)&&(process.exitCode=1)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as s,renameSync as
|
|
1
|
+
import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as s,renameSync as o,unlinkSync as i}from"fs";import{join as r,dirname as a}from"path";import{homedir as g}from"os";import{logger as c}from"../utils/logger.js";import{configManager as l,DEFAULT_MODELS as C}from"./config.js";import{loadSettingsTemplate as d}from"./settings-template.js";import{cleanupAnthropicEnvVars as h}from"./env-cleanup.js";export const MODEL_ENV_VARS=["ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];export class ClaudeCodeManager{static instance;settingsPath;mcpConfigPath;constructor(){this.settingsPath=r(g(),".claude","settings.json"),this.mcpConfigPath=r(g(),".claude.json")}static getInstance(){return ClaudeCodeManager.instance||(ClaudeCodeManager.instance=new ClaudeCodeManager),ClaudeCodeManager.instance}ensureConfigDir(n){const s=a(n);e(s)||t(s,{recursive:!0})}atomicWrite(t,n){const r=`${t}.tmp`;try{s(r,n,"utf-8"),o(r,t)}catch(t){if(e(r))try{i(r)}catch{}throw t}}getSettings(){try{if(e(this.settingsPath)){const e=n(this.settingsPath,"utf-8");return JSON.parse(e)}}catch(e){c.warn("Failed to read Claude Code settings:",e),c.logError("ClaudeCodeManager.getSettings",e)}return{}}saveSettings(e){try{this.ensureConfigDir(this.settingsPath),this.atomicWrite(this.settingsPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code settings: ${e}`)}}getMCPConfig(){try{if(e(this.mcpConfigPath)){const e=n(this.mcpConfigPath,"utf-8");return JSON.parse(e)}}catch(e){c.warn("Failed to read Claude Code MCP config:",e),c.logError("ClaudeCodeManager.getMCPConfig",e)}return{}}saveMCPConfig(e){try{this.ensureConfigDir(this.mcpConfigPath),this.atomicWrite(this.mcpConfigPath,JSON.stringify(e,null,2))}catch(e){throw new Error(`Failed to save Claude Code MCP config: ${e}`)}}loadPrivateConfig(e,t){this.ensureOnboardingCompleted(),h();const n=d(),s=n.env||{},{env:o,...i}=n,r=this.getSettings(),{ANTHROPIC_API_KEY:a,...g}=r.env||{},c={...g};for(const e of MODEL_ENV_VARS)delete c[e];const u={},O=e=>{const n=(t[e]||"").trim();return n&&n!==C[e]?n:null},f=O("haiku"),_=O("sonnet"),m=O("opus");f&&(u.ANTHROPIC_DEFAULT_HAIKU_MODEL=f),_&&(u.ANTHROPIC_DEFAULT_SONNET_MODEL=_),m&&(u.ANTHROPIC_DEFAULT_OPUS_MODEL=m,u.ANTHROPIC_MODEL=m);const E={...r};for(const[e,t]of Object.entries(i))e in E||(E[e]=t);E.showThinkingSummaries=!0;const P={...E,env:{...c,...Object.fromEntries(Object.entries(s).filter(([e])=>!(e in c))),ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_BASE_URL:l.getBaseUrl(),...u}};m?P.model=m:Object.prototype.hasOwnProperty.call(i,"model")?P.model=i.model:delete P.model,this.saveSettings(P)}ensureOnboardingCompleted(){try{const e=this.getMCPConfig();e.hasCompletedOnboarding||this.saveMCPConfig({...e,hasCompletedOnboarding:!0})}catch(e){c.warn("Failed to ensure onboarding completed:",e),c.logError("ClaudeCodeManager.ensureOnboardingCompleted",e)}}unloadPrivateConfig(){const e=this.getSettings();if(!e.env){if(void 0!==e.model){const{model:t,...n}=e;this.saveSettings(n)}return}const{ANTHROPIC_AUTH_TOKEN:t,ANTHROPIC_BASE_URL:n,API_TIMEOUT_MS:s,CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:o,...i}=e.env,r={...i};for(const e of MODEL_ENV_VARS)delete r[e];const a={...e,env:r};delete a.model,a.env&&0===Object.keys(a.env).length&&delete a.env,this.saveSettings(a)}clearPinnedModels(){const e=this.getSettings(),t=e.env,n=[];for(const e of MODEL_ENV_VARS)t&&e in t&&n.push(e);if(void 0!==e.model&&n.push("model"),0===n.length)return[];const s={...e};if(delete s.model,t){const e={...t};for(const t of MODEL_ENV_VARS)delete e[t];0===Object.keys(e).length?delete s.env:s.env=e}return this.saveSettings(s),n}detectCurrentConfig(){try{const e=this.getSettings();return e.env&&e.env.ANTHROPIC_AUTH_TOKEN?{apiKey:e.env.ANTHROPIC_AUTH_TOKEN}:{apiKey:null}}catch{return{apiKey:null}}}getExperimentalFeature(e){const t=this.getSettings();return t.env?.[e]}setExperimentalFeature(e,t){const n=this.getSettings(),s=n.env||{},o={...n,env:{...s,[e]:t}};this.saveSettings(o)}removeExperimentalFeature(e){const t=this.getSettings();if(!t.env)return;const{[e]:n,...s}=t.env,o={...t,env:s};this.saveSettings(o)}}export const claudeCodeManager=ClaudeCodeManager.getInstance();
|
package/dist/lib/command.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Command as
|
|
1
|
+
import{Command as e}from"commander";import{i18n as a,detectSystemLang as o}from"./i18n.js";import{configManager as t}from"./config.js";import{wizard as n}from"./wizard.js";import{langCommand as i,authCommand as s,doctorCommand as r,configCommand as c,statusCommand as m}from"../commands/index.js";import{logsCommand as p}from"../commands/logs.js";import{completionCommand as l}from"../commands/completion.js";import{benchCommand as d}from"../commands/bench.js";import{updateCommand as g}from"../commands/update.js";import{setupCommand as u}from"../commands/setup.js";import h from"chalk";import{readFileSync as y}from"fs";import{join as f,dirname as b}from"path";import{fileURLToPath as k}from"url";import{setRuntimeOptions as v,detectColorPreferenceFromEnv as w,isColorEnabled as $,isJsonMode as j}from"./runtime-options.js";import{cleanupAnthropicEnvVars as x}from"./env-cleanup.js";import{runStartupUpdate as C}from"./auto-update.js";export class Command{program;constructor(){const n=t.isFirstRun()?o():t.getLang();a.loadFromConfig(n),this.program=new e,this.setupProgram()}getVersion(){try{const e=k(import.meta.url),a=b(e),o=f(a,"../../package.json");return JSON.parse(y(o,"utf-8")).version}catch{return"1.0.0"}}setupProgram(){this.program.name("vibecli").description(a.t("cli.title")).version(this.getVersion(),"-v, --version",a.t("commands.version")).helpOption("-h, --help",a.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(a.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("--1m","Enable the 1M context window for Codex").option("--all","Configure every tool the key can serve (default behavior)").action(async e=>{await u({key:e.key,claudekey:e.claudekey,codexkey:e.codexkey,all:e.all,oneMillion:e["1m"]})}),this.program.command("init").description(a.t("commands.init")).action(async()=>{await this.handleInitCommand()});const e=this.program.command("lang").description(a.t("commands.lang"));e.command("show").description(a.t("lang.show_usage")).action(async()=>{await i(["show"])}),e.command("set <locale>").description(a.t("lang.set_usage")).action(async e=>{await i(["set",e])});const o=this.program.command("auth").description(a.t("commands.auth"));o.argument("[token]","API token").action(async e=>{const a=[];e&&a.push(e),await s(a)}),o.command("revoke").description("Revoke saved API key").action(async()=>{await s(["revoke"])}),o.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(a.t("commands.doctor")).action(async()=>{await r()}),this.program.command("status").description(a.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,a)=>{const o=[];e&&o.push(e),a&&o.push(a),await p(o)}),this.program.command("completion <shell>").description("Print shell completion script (bash|zsh|fish)").action(async e=>{await l([e])}),this.program.command("update").description("Check for a newer version across CNB and npm mirrors").action(async()=>{await g()}),this.program.command("bench").description("Benchmark the configured API endpoint").option("--samples <n>","number of samples to send","5").action(async e=>{const a=[];e.samples&&a.push("--samples",e.samples),await d(a)}),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(a.t("commands.enter")).action(async e=>{if(e)switch(e){case"lang":case"language":await n.configLanguage();break;case"apikey":case"api-key":await n.configApiKey();break;case"models":await n.configModels();break;default:{const a=[e];await c(a);break}}else await n.run()}),this.program.action(async()=>{await n.run()}),this.program.configureHelp({sortSubcommands:!0,subcommandTerm:e=>e.name()+" "+e.usage()}),this.program.addHelpText("after",`\n${h.bold(a.t("cli.examples"))}:\n ${h.gray("$ vibecli # Interactive main menu")}\n ${h.gray("$ vibecli init # Run first-time setup wizard")}\n ${h.gray("$ vibecli setup --key sk-xxx # One-shot setup (auto-detect capable tools)")}\n ${h.gray("$ vibecli setup --claudekey sk-a --codexkey sk-b")}\n ${h.gray("$ vibecli status --json # Machine-readable status")}\n ${h.gray("$ vibecli config profile list")}\n ${h.gray("$ vibecli config profile create work")}\n ${h.gray("$ vibecli config profile use work")}\n ${h.gray("$ vibecli config preset save fast")}\n ${h.gray("$ vibecli config backup create snapshot")}\n ${h.gray("$ vibecli auth <token> --non-interactive")}\n ${h.gray("$ vibecli auth revoke --yes")}\n ${h.gray("$ vibecli logs path")}\n ${h.gray("$ vibecli completion bash")}\n ${h.gray("$ vibecli bench --samples 3")}\n ${h.gray("$ vibecli update")}\n ${h.gray('$ vibecli --base "https://custom.api.com" init # Custom base URL')}\n`)}async handleInitCommand(){await n.runFirstTimeSetup()}cleanupLeakedEnvVars(){const e=x();if(0===e.length||j())return;const o=a.t("messages.env_vars_cleared",{vars:e.join(", ")});console.log($()?h.yellow(o):o)}preflightArgs(e){const a=a=>e.includes(a),o=!a("--no-color")&&w();let n;v({nonInteractive:a("--non-interactive"),json:a("--json"),yes:a("--yes")||a("-y"),dryRun:a("--dry-run"),color:o}),o||(h.level=0);for(let a=0;a<e.length;a+=1){const o=e[a];if("--base"===o&&e[a+1]){n=e[a+1];break}if(o.startsWith("--base=")){n=o.slice(7);break}}n&&t.setBaseUrl(n)}async execute(e){try{if(this.preflightArgs(e),this.cleanupLeakedEnvVars(),(await C(e)).handled)return;await this.program.parseAsync(e,{from:"user"})}catch(e){if(e instanceof Error){const o=$()?h.red:e=>e;console.error(o(a.t("cli.error_general")),e.message)}process.exit(1)}}getProgram(){return this.program}}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface VibecliModels {
|
|
|
9
9
|
/** Additional custom slots (e.g. a "vision" or "fast" slot) are preserved as-is. */
|
|
10
10
|
[slot: string]: string;
|
|
11
11
|
}
|
|
12
|
+
export type VibecliToolSettingValue = string | number | boolean;
|
|
13
|
+
export type VibecliToolSettings = Record<string, VibecliToolSettingValue>;
|
|
12
14
|
export interface VibecliProfile {
|
|
13
15
|
api_key?: string;
|
|
14
16
|
/** Per-tool credentials used by setup --claudekey/--codexkey. */
|
|
@@ -17,6 +19,8 @@ export interface VibecliProfile {
|
|
|
17
19
|
models?: VibecliModels;
|
|
18
20
|
/** Per-tool model overrides, merged over `models` when configuring that tool. */
|
|
19
21
|
tool_models?: Record<string, Partial<VibecliModels>>;
|
|
22
|
+
/** Per-tool behavior settings, such as Codex's optional 1M context window. */
|
|
23
|
+
tool_settings?: Record<string, VibecliToolSettings>;
|
|
20
24
|
}
|
|
21
25
|
export interface VibecliPreset {
|
|
22
26
|
models: VibecliModels;
|
|
@@ -56,6 +60,7 @@ export declare class ConfigManager {
|
|
|
56
60
|
private createDefaultConfig;
|
|
57
61
|
private normalizeModels;
|
|
58
62
|
private normalizeToolModels;
|
|
63
|
+
private normalizeToolSettings;
|
|
59
64
|
private normalizeStringRecord;
|
|
60
65
|
private normalizeProfile;
|
|
61
66
|
private normalizeProfiles;
|
|
@@ -112,6 +117,9 @@ export declare class ConfigManager {
|
|
|
112
117
|
getModelsForTool(toolName: string): VibecliModels;
|
|
113
118
|
/** Set (or clear) a per-tool model override on the active profile. */
|
|
114
119
|
setToolModel(toolName: string, override: Partial<VibecliModels> | null): void;
|
|
120
|
+
getToolSettings(toolName: string): VibecliToolSettings;
|
|
121
|
+
getToolSetting(toolName: string, key: string): VibecliToolSettingValue | undefined;
|
|
122
|
+
setToolSetting(toolName: string, key: string, value: VibecliToolSettingValue): void;
|
|
115
123
|
getCustomPresets(): Record<string, VibecliPreset>;
|
|
116
124
|
saveCustomPreset(name: string, models: typeof DEFAULT_MODELS): void;
|
|
117
125
|
deleteCustomPreset(name: string): void;
|
package/dist/lib/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,mkdirSync as t,readFileSync as i,writeFileSync as o,renameSync as s,unlinkSync as r,readdirSync as n,rmdirSync as a,statSync as c,chmodSync as l}from"fs";import{join as f,dirname as g}from"path";import{homedir as h}from"os";import{createHash as p}from"crypto";import*as m from"js-yaml";import{logger as u}from"../utils/logger.js";import{getDefaultModels as d}from"./model-registry.js";import{readLegacySecretsFile as y}from"./secret-store.js";export const DEFAULT_BASE_URL="https://www.vibeapi.cn";export const DEFAULT_MODELS=d();export const DEFAULT_PROFILE_NAME="default";const _="plaintext";export class ConfigManager{static instance;configDir;configPath;config;runtimeBaseUrl;constructor(){this.configDir=this.resolveConfigDir(),this.migrateLegacyConfigDir(),this.configPath=f(this.configDir,"config.yaml"),this.config=this.loadConfig()}migrateLegacyConfigDir(){try{const t=f(g(this.configDir),".chelper");if(t===this.configDir||!e(t))return;if(["config.yaml","secrets.enc"].some(t=>e(f(this.configDir,t))))return;if(e(this.configDir)){for(const i of n(t)){const o=f(this.configDir,i);e(o)||s(f(t,i),o)}0===n(t).length&&a(t)}else s(t,this.configDir);u.info(`Migrated config dir: ${t} -> ${this.configDir}`)}catch(e){u.warn("Failed to migrate legacy .chelper config dir:",e)}}resolveConfigDir(){const e=process.env.VIBECLI_CONFIG_DIR;if(e&&e.trim())return e.trim();const t=process.env.VIBECLI_TEST_HOME;return t&&t.trim()?f(t.trim(),".vibecli"):f(h(),".vibecli")}createDefaultConfig(){return this.syncLegacyFields({lang:"en_US",active_profile:"default",profiles:{[DEFAULT_PROFILE_NAME]:{}},presets:{}})}normalizeModels(e){const t={haiku:e?.haiku?.trim()||DEFAULT_MODELS.haiku,sonnet:e?.sonnet?.trim()||DEFAULT_MODELS.sonnet,opus:e?.opus?.trim()||DEFAULT_MODELS.opus,reasoning:e?.reasoning?.trim()||DEFAULT_MODELS.reasoning};if(e&&"object"==typeof e)for(const[i,o]of Object.entries(e))i in t||"string"==typeof o&&o.trim()&&(t[i]=o.trim());return t}normalizeToolModels(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||"string"!=typeof e||!t||"object"!=typeof t)return[];const i={};for(const[e,o]of Object.entries(t))"string"==typeof o&&o.trim()&&(i[e]=o.trim());return Object.keys(i).length>0?[[e,i]]:[]});return Object.fromEntries(t)}normalizeStringRecord(e){return!e||"object"!=typeof e||Array.isArray(e)?{}:Object.fromEntries(Object.entries(e).filter(e=>"string"==typeof e[1]&&Boolean(e[1].trim())).map(([e,t])=>[e,t.trim()]))}normalizeProfile(e){if(!e)return{};const t=this.normalizeToolModels(e.tool_models),i=this.normalizeStringRecord(e.tool_api_keys);return{..."string"==typeof e.api_key&&e.api_key?{api_key:e.api_key}:{},...Object.keys(i).length>0?{tool_api_keys:i}:{},..."string"==typeof e.base_url&&e.base_url?{base_url:e.base_url}:{},...e.models?{models:this.normalizeModels(e.models)}:{},...Object.keys(t).length>0?{tool_models:t}:{}}}normalizeProfiles(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>e&&"string"==typeof e&&t&&"object"==typeof t?[[e,this.normalizeProfile(t)]]:[]);return Object.fromEntries(t)}normalizePresets(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||"string"!=typeof e||!t||"object"!=typeof t)return[];const i=t;return i.models?[[e,{models:this.normalizeModels(i.models),updated_at:i.updated_at||new Date(0).toISOString()}]]:[]});return Object.fromEntries(t)}getResolvedActiveProfile(e){const t=e.active_profile||"default";return e.profiles?.[t]||{}}syncLegacyFields(e){const t=this.normalizeProfiles(e.profiles),i=e.active_profile?.trim()||Object.keys(t)[0]||"default",o=t[i]?t:{...t,[i]:{}},s=this.getResolvedActiveProfile({...e,profiles:o,active_profile:i});return{...e,lang:e.lang||"en_US",active_profile:i,profiles:o,presets:this.normalizePresets(e.presets),api_key:s.api_key,base_url:s.base_url,models:s.models?this.normalizeModels(s.models):DEFAULT_MODELS}}sanitizeBackupLabel(e){return(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)}getBackupDir(){return f(this.configDir,"backups")}getLegacySecretsPath(){return f(this.configDir,"secrets.enc")}getSecretsBackupPath(e){return e.replace(/\.yaml$/,".secrets.enc")}static getInstance(){return ConfigManager.instance||(ConfigManager.instance=new ConfigManager),ConfigManager.instance}ensureConfigDir(){e(this.configDir)||t(this.configDir,{recursive:!0})}loadConfig(){try{if(e(this.configPath)){const t=i(this.configPath,"utf-8"),o=m.load(t);let s=this.migrateOldConfig(o||{lang:"en_US"}),n=!1;const a=this.getLegacySecretsPath();if(e(a)&&o?.credential_store!==_)try{s=this.mergeLegacySecrets(s,y(a)),n=!0}catch(e){u.warn("Legacy secrets.enc is unreadable on this host; it was kept for recovery and will not block new credentials."),u.logError("ConfigManager.migrateLegacySecrets",e)}if((this.needsMigration(o)||n)&&(this.backupConfig(),this.atomicWrite(this.configPath,m.dump(this.prepareForPersistence(s))),n))try{r(a)}catch(e){u.logError("ConfigManager.removeLegacySecrets",e)}try{l(this.configPath,384)}catch{}return s}const t=this.getLegacySecretsPath();if(e(t))try{const e=this.mergeLegacySecrets(this.createDefaultConfig(),y(t));this.ensureConfigDir(),this.atomicWrite(this.configPath,m.dump(this.prepareForPersistence(e)));try{r(t)}catch(e){u.logError("ConfigManager.removeLegacySecrets",e)}return e}catch(e){u.warn("Legacy secrets.enc is unreadable on this host; it was kept for recovery and will not block new credentials."),u.logError("ConfigManager.migrateLegacySecretsWithoutConfig",e)}}catch(e){u.warn("Failed to load config, using defaults:",e),u.logError("ConfigManager.loadConfig",e)}return this.createDefaultConfig()}needsMigration(e){return!(!e||"object"!=typeof e)&&("plan"in e||!("profiles"in e)||!("active_profile"in e)||!("presets"in e))}migrateOldConfig(e){if(!e||"object"!=typeof e)return this.createDefaultConfig();const t=e,{plan:i,...o}=t,s=this.normalizeProfiles(o.profiles),r=this.normalizeProfile({api_key:"string"==typeof o.api_key?o.api_key:void 0,base_url:"string"==typeof o.base_url?o.base_url:void 0,models:o.models}),n=Object.keys(s).length>0?s:{[DEFAULT_PROFILE_NAME]:r},a="string"==typeof o.active_profile&&o.active_profile.trim()?o.active_profile.trim():Object.keys(n)[0]||"default";return this.syncLegacyFields({lang:"string"==typeof o.lang&&o.lang?o.lang:"en_US",...o.credential_store===_?{credential_store:_}:{},active_profile:a,profiles:n,presets:this.normalizePresets(o.presets)})}atomicWrite(t,i){const n=`${t}.tmp`;try{o(n,i,{encoding:"utf-8",mode:384}),s(n,t);try{l(t,384)}catch{}}catch(t){if(e(n))try{r(n)}catch{}throw t}}backupConfig(){if(!e(this.configPath))return;const s=this.getBackupDir();e(s)||t(s,{recursive:!0});const r=(new Date).toISOString().replace(/[:.]/g,"-"),n=f(s,`config-${r}.yaml`);try{const e=i(this.configPath,"utf-8");o(n,e,{encoding:"utf-8",mode:384}),this.rotateBackups(s,10,30)}catch(e){u.logError("ConfigManager.backupConfig",e)}}rotateBackups(t,i,o){try{const s=Date.now()-24*o*60*60*1e3,a=n(t).filter(e=>e.startsWith("config-")&&e.endsWith(".yaml")).map(e=>({name:e,path:f(t,e),mtime:c(f(t,e)).mtime})).sort((e,t)=>t.mtime.getTime()-e.mtime.getTime()),l=new Set;a.slice(i).forEach(e=>l.add(e.path)),a.forEach(e=>{e.mtime.getTime()<s&&l.add(e.path)});for(const t of l){let i=!1;try{r(t),i=!0}catch(e){u.logError("ConfigManager.rotateBackups",e)}if(i){const i=this.getSecretsBackupPath(t);if(e(i))try{r(i)}catch(e){u.logError("ConfigManager.rotateBackups.secrets",e)}}}}catch(e){u.logError("ConfigManager.rotateBackups",e)}}saveConfig(e){try{this.ensureConfigDir(),this.backupConfig();const t=this.syncLegacyFields(this.migrateOldConfig(e||this.config)),i=this.prepareForPersistence(t),o=m.dump(i);this.atomicWrite(this.configPath,o),this.config=t}catch(e){throw u.error("Failed to save config:",e),u.logError("ConfigManager.saveConfig",e),e}}prepareForPersistence(e){const t={...e,credential_store:_};return delete t.api_key,delete t.base_url,delete t.models,t}mergeLegacySecrets(e,t){const i={...e.profiles||{}},o=new Set([...Object.keys(t.api_keys||{}),...Object.keys(t.tool_api_keys||{})]);for(const e of o){const o=i[e]||{},s=o.api_key||t.api_keys?.[e],r={...t.tool_api_keys?.[e]||{},...o.tool_api_keys||{}};i[e]={...o,...s?{api_key:s}:{},...Object.keys(r).length>0?{tool_api_keys:r}:{}}}return this.syncLegacyFields({...e,profiles:i})}getConfig(){return this.syncLegacyFields({...this.config})}updateConfig(e){this.saveConfig({...this.config,...e})}isFirstRun(){return!e(this.configPath)}getLang(){return this.config.lang||"en_US"}setLang(e){this.updateConfig({lang:e})}getActiveProfileName(){return this.config.active_profile||"default"}listProfiles(){return Object.keys(this.config.profiles||{}).sort()}getProfile(e=this.getActiveProfileName()){const t=this.config.profiles?.[e]||{};return{...t.api_key?{api_key:t.api_key}:{},...t.tool_api_keys&&Object.keys(t.tool_api_keys).length>0?{tool_api_keys:this.normalizeStringRecord(t.tool_api_keys)}:{},...t.base_url?{base_url:t.base_url}:{},models:t.models?this.normalizeModels(t.models):DEFAULT_MODELS,...t.tool_models&&Object.keys(t.tool_models).length>0?{tool_models:this.normalizeToolModels(t.tool_models)}:{}}}createProfile(e,t=this.getActiveProfileName()){const i=this.getProfile(t),o={...this.config.profiles||{},[e]:{...i.api_key?{api_key:i.api_key}:{},...i.base_url?{base_url:i.base_url}:{},models:this.normalizeModels(i.models)}};this.saveConfig({...this.config,profiles:o})}setActiveProfile(e){if(!this.config.profiles?.[e])throw new Error(`Profile not found: ${e}`);this.saveConfig({...this.config,active_profile:e})}updateProfile(e,t){const i=this.config.profiles?.[e]||{},o=t.models?this.normalizeModels({...i.models,...t.models}):i.models?this.normalizeModels(i.models):void 0,s={...i,...void 0!==t.api_key?{api_key:t.api_key}:{},...void 0!==t.base_url?{base_url:t.base_url}:{},...o?{models:o}:{}},r={...this.config.profiles||{},[e]:s};this.saveConfig({...this.config,profiles:r})}deleteProfile(e){const t={...this.config.profiles||{}};if(!t[e])return;const i=Object.keys(t).filter(t=>t!==e);if(0===i.length)throw new Error("At least one profile must remain");delete t[e];const o=this.getActiveProfileName()===e?i[0]:this.getActiveProfileName();this.saveConfig({...this.config,profiles:t,active_profile:o})}getApiKey(){return this.config.profiles?.[this.getActiveProfileName()]?.api_key}setApiKey(e){this.updateProfile(this.getActiveProfileName(),{api_key:e})}revokeApiKey(){const e=this.config.profiles?.[this.getActiveProfileName()]||{},{api_key:t,tool_api_keys:i,...o}=e,s={...this.config.profiles||{},[this.getActiveProfileName()]:o};this.saveConfig({...this.config,profiles:s})}getApiKeyFingerprint(){const e=this.getApiKey();return e?p("sha256").update(e).digest("hex").slice(0,12):""}getApiKeyForTool(e){const t=this.config.profiles?.[this.getActiveProfileName()];return t?.tool_api_keys?.[e]||t?.api_key||void 0}setToolApiKey(e,t){const i=this.getActiveProfileName(),o=this.config.profiles?.[i]||{},s={...this.config.profiles||{},[i]:{...o,tool_api_keys:{...o.tool_api_keys||{},[e]:t}}};this.saveConfig({...this.config,profiles:s})}clearToolApiKey(e){const t=this.getActiveProfileName(),i=this.config.profiles?.[t]||{},{[e]:o,...s}=i.tool_api_keys||{},r={...i};Object.keys(s).length>0?r.tool_api_keys=s:delete r.tool_api_keys,this.saveConfig({...this.config,profiles:{...this.config.profiles||{},[t]:r}})}listToolApiKeys(){return Object.keys(this.config.profiles?.[this.getActiveProfileName()]?.tool_api_keys||{}).sort()}getToolApiKeyFingerprint(e){const t=this.config.profiles?.[this.getActiveProfileName()]?.tool_api_keys?.[e];return t?p("sha256").update(t).digest("hex").slice(0,12):""}getModels(){const e=this.config.profiles?.[this.getActiveProfileName()]?.models;return e?this.normalizeModels(e):DEFAULT_MODELS}setModels(e){this.updateProfile(this.getActiveProfileName(),{models:this.normalizeModels(e)})}updateModels(e){this.setModels(this.normalizeModels({...this.getModels(),...e}))}getModelsForTool(e){const t=this.getModels(),i=this.config.profiles?.[this.getActiveProfileName()]?.tool_models?.[e];return i?this.normalizeModels({...t,...i}):t}setToolModel(e,t){const i=this.getActiveProfileName(),o=this.config.profiles?.[i]||{},s={...o.tool_models||{}};t&&0!==Object.keys(t).length?s[e]=t:delete s[e];const r={...o};Object.keys(s).length>0?r.tool_models=s:delete r.tool_models;const n={...this.config.profiles||{},[i]:r};this.saveConfig({...this.config,profiles:n})}getCustomPresets(){return{...this.config.presets||{}}}saveCustomPreset(e,t){const i={...this.config.presets||{},[e]:{models:this.normalizeModels(t),updated_at:(new Date).toISOString()}};this.saveConfig({...this.config,presets:i})}deleteCustomPreset(e){const t={...this.config.presets||{}};t[e]&&(delete t[e],this.saveConfig({...this.config,presets:t}))}listBackups(){const t=this.getBackupDir();return e(t)?n(t).filter(e=>e.startsWith("config-")&&e.endsWith(".yaml")).map(e=>{const i=f(t,e);return{name:e,path:i,mtimeMs:c(i).mtimeMs}}).sort((e,t)=>t.mtimeMs-e.mtimeMs):[]}createBackup(s){this.ensureConfigDir();const r=this.getBackupDir();e(r)||t(r,{recursive:!0});const n=(new Date).toISOString().replace(/[:.]/g,"-"),a=this.sanitizeBackupLabel(s),c=f(r,a?`config-${n}-${a}.yaml`:`config-${n}.yaml`),l=e(this.configPath)?i(this.configPath,"utf-8"):m.dump(this.prepareForPersistence(this.getConfig()));return o(c,l,{encoding:"utf-8",mode:384}),this.rotateBackups(r,10,30),c}restoreBackup(t){if(!t||"."===t||".."===t||/[/\\]|\.\./.test(t))throw new Error(`Invalid backup name: ${t}`);const o=this.getBackupDir(),s=f(o,t);if(!e(s))throw new Error(`Backup not found: ${t}`);const r=i(s,"utf-8"),n=m.load(r),a=this.getSecretsBackupPath(s);let c=this.migrateOldConfig(n||this.createDefaultConfig());if(n?.credential_store!==_&&e(a))try{c=this.mergeLegacySecrets(c,y(a))}catch(e){throw new Error(`Legacy backup credentials could not be decrypted: ${a}`,{cause:e})}this.saveConfig(c)}getBaseUrl(){return this.runtimeBaseUrl||this.config.profiles?.[this.getActiveProfileName()]?.base_url||DEFAULT_BASE_URL}setBaseUrl(e,t=!1){this.runtimeBaseUrl=e,t&&this.updateProfile(this.getActiveProfileName(),{base_url:e})}resetBaseUrl(){this.runtimeBaseUrl=void 0;const e=this.config.profiles?.[this.getActiveProfileName()]||{},{base_url:t,...i}=e,o={...this.config.profiles||{},[this.getActiveProfileName()]:i};this.saveConfig({...this.config,profiles:o})}}export const configManager=ConfigManager.getInstance();
|
|
1
|
+
import{existsSync as e,mkdirSync as t,readFileSync as i,writeFileSync as o,renameSync as s,unlinkSync as r,readdirSync as n,rmdirSync as a,statSync as c,chmodSync as l}from"fs";import{join as f,dirname as g}from"path";import{homedir as h}from"os";import{createHash as p}from"crypto";import*as m from"js-yaml";import{logger as u}from"../utils/logger.js";import{getDefaultModels as y}from"./model-registry.js";import{readLegacySecretsFile as d}from"./secret-store.js";export const DEFAULT_BASE_URL="https://www.vibeapi.cn";export const DEFAULT_MODELS=y();export const DEFAULT_PROFILE_NAME="default";const _="plaintext";export class ConfigManager{static instance;configDir;configPath;config;runtimeBaseUrl;constructor(){this.configDir=this.resolveConfigDir(),this.migrateLegacyConfigDir(),this.configPath=f(this.configDir,"config.yaml"),this.config=this.loadConfig()}migrateLegacyConfigDir(){try{const t=f(g(this.configDir),".chelper");if(t===this.configDir||!e(t))return;if(["config.yaml","secrets.enc"].some(t=>e(f(this.configDir,t))))return;if(e(this.configDir)){for(const i of n(t)){const o=f(this.configDir,i);e(o)||s(f(t,i),o)}0===n(t).length&&a(t)}else s(t,this.configDir);u.info(`Migrated config dir: ${t} -> ${this.configDir}`)}catch(e){u.warn("Failed to migrate legacy .chelper config dir:",e)}}resolveConfigDir(){const e=process.env.VIBECLI_CONFIG_DIR;if(e&&e.trim())return e.trim();const t=process.env.VIBECLI_TEST_HOME;return t&&t.trim()?f(t.trim(),".vibecli"):f(h(),".vibecli")}createDefaultConfig(){return this.syncLegacyFields({lang:"en_US",active_profile:"default",profiles:{[DEFAULT_PROFILE_NAME]:{}},presets:{}})}normalizeModels(e){const t={haiku:e?.haiku?.trim()||DEFAULT_MODELS.haiku,sonnet:e?.sonnet?.trim()||DEFAULT_MODELS.sonnet,opus:e?.opus?.trim()||DEFAULT_MODELS.opus,reasoning:e?.reasoning?.trim()||DEFAULT_MODELS.reasoning};if(e&&"object"==typeof e)for(const[i,o]of Object.entries(e))i in t||"string"==typeof o&&o.trim()&&(t[i]=o.trim());return t}normalizeToolModels(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||"string"!=typeof e||!t||"object"!=typeof t)return[];const i={};for(const[e,o]of Object.entries(t))"string"==typeof o&&o.trim()&&(i[e]=o.trim());return Object.keys(i).length>0?[[e,i]]:[]});return Object.fromEntries(t)}normalizeToolSettings(e){if(!e||"object"!=typeof e||Array.isArray(e))return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||!t||"object"!=typeof t||Array.isArray(t))return[];const i=Object.fromEntries(Object.entries(t).filter(([,e])=>"string"==typeof e||"boolean"==typeof e||"number"==typeof e&&Number.isFinite(e)));return Object.keys(i).length>0?[[e,i]]:[]});return Object.fromEntries(t)}normalizeStringRecord(e){return!e||"object"!=typeof e||Array.isArray(e)?{}:Object.fromEntries(Object.entries(e).filter(e=>"string"==typeof e[1]&&Boolean(e[1].trim())).map(([e,t])=>[e,t.trim()]))}normalizeProfile(e){if(!e)return{};const t=this.normalizeToolModels(e.tool_models),i=this.normalizeToolSettings(e.tool_settings),o=this.normalizeStringRecord(e.tool_api_keys);return{..."string"==typeof e.api_key&&e.api_key?{api_key:e.api_key}:{},...Object.keys(o).length>0?{tool_api_keys:o}:{},..."string"==typeof e.base_url&&e.base_url?{base_url:e.base_url}:{},...e.models?{models:this.normalizeModels(e.models)}:{},...Object.keys(t).length>0?{tool_models:t}:{},...Object.keys(i).length>0?{tool_settings:i}:{}}}normalizeProfiles(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>e&&"string"==typeof e&&t&&"object"==typeof t?[[e,this.normalizeProfile(t)]]:[]);return Object.fromEntries(t)}normalizePresets(e){if(!e||"object"!=typeof e)return{};const t=Object.entries(e).flatMap(([e,t])=>{if(!e||"string"!=typeof e||!t||"object"!=typeof t)return[];const i=t;return i.models?[[e,{models:this.normalizeModels(i.models),updated_at:i.updated_at||new Date(0).toISOString()}]]:[]});return Object.fromEntries(t)}getResolvedActiveProfile(e){const t=e.active_profile||"default";return e.profiles?.[t]||{}}syncLegacyFields(e){const t=this.normalizeProfiles(e.profiles),i=e.active_profile?.trim()||Object.keys(t)[0]||"default",o=t[i]?t:{...t,[i]:{}},s=this.getResolvedActiveProfile({...e,profiles:o,active_profile:i});return{...e,lang:e.lang||"en_US",active_profile:i,profiles:o,presets:this.normalizePresets(e.presets),api_key:s.api_key,base_url:s.base_url,models:s.models?this.normalizeModels(s.models):DEFAULT_MODELS}}sanitizeBackupLabel(e){return(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)}getBackupDir(){return f(this.configDir,"backups")}getLegacySecretsPath(){return f(this.configDir,"secrets.enc")}getSecretsBackupPath(e){return e.replace(/\.yaml$/,".secrets.enc")}static getInstance(){return ConfigManager.instance||(ConfigManager.instance=new ConfigManager),ConfigManager.instance}ensureConfigDir(){e(this.configDir)||t(this.configDir,{recursive:!0})}loadConfig(){try{if(e(this.configPath)){const t=i(this.configPath,"utf-8"),o=m.load(t);let s=this.migrateOldConfig(o||{lang:"en_US"}),n=!1;const a=this.getLegacySecretsPath();if(e(a)&&o?.credential_store!==_)try{s=this.mergeLegacySecrets(s,d(a)),n=!0}catch(e){u.warn("Legacy secrets.enc is unreadable on this host; it was kept for recovery and will not block new credentials."),u.logError("ConfigManager.migrateLegacySecrets",e)}if((this.needsMigration(o)||n)&&(this.backupConfig(),this.atomicWrite(this.configPath,m.dump(this.prepareForPersistence(s))),n))try{r(a)}catch(e){u.logError("ConfigManager.removeLegacySecrets",e)}try{l(this.configPath,384)}catch{}return s}const t=this.getLegacySecretsPath();if(e(t))try{const e=this.mergeLegacySecrets(this.createDefaultConfig(),d(t));this.ensureConfigDir(),this.atomicWrite(this.configPath,m.dump(this.prepareForPersistence(e)));try{r(t)}catch(e){u.logError("ConfigManager.removeLegacySecrets",e)}return e}catch(e){u.warn("Legacy secrets.enc is unreadable on this host; it was kept for recovery and will not block new credentials."),u.logError("ConfigManager.migrateLegacySecretsWithoutConfig",e)}}catch(e){u.warn("Failed to load config, using defaults:",e),u.logError("ConfigManager.loadConfig",e)}return this.createDefaultConfig()}needsMigration(e){return!(!e||"object"!=typeof e)&&("plan"in e||!("profiles"in e)||!("active_profile"in e)||!("presets"in e))}migrateOldConfig(e){if(!e||"object"!=typeof e)return this.createDefaultConfig();const t=e,{plan:i,...o}=t,s=this.normalizeProfiles(o.profiles),r=this.normalizeProfile({api_key:"string"==typeof o.api_key?o.api_key:void 0,base_url:"string"==typeof o.base_url?o.base_url:void 0,models:o.models}),n=Object.keys(s).length>0?s:{[DEFAULT_PROFILE_NAME]:r},a="string"==typeof o.active_profile&&o.active_profile.trim()?o.active_profile.trim():Object.keys(n)[0]||"default";return this.syncLegacyFields({lang:"string"==typeof o.lang&&o.lang?o.lang:"en_US",...o.credential_store===_?{credential_store:_}:{},active_profile:a,profiles:n,presets:this.normalizePresets(o.presets)})}atomicWrite(t,i){const n=`${t}.tmp`;try{o(n,i,{encoding:"utf-8",mode:384}),s(n,t);try{l(t,384)}catch{}}catch(t){if(e(n))try{r(n)}catch{}throw t}}backupConfig(){if(!e(this.configPath))return;const s=this.getBackupDir();e(s)||t(s,{recursive:!0});const r=(new Date).toISOString().replace(/[:.]/g,"-"),n=f(s,`config-${r}.yaml`);try{const e=i(this.configPath,"utf-8");o(n,e,{encoding:"utf-8",mode:384}),this.rotateBackups(s,10,30)}catch(e){u.logError("ConfigManager.backupConfig",e)}}rotateBackups(t,i,o){try{const s=Date.now()-24*o*60*60*1e3,a=n(t).filter(e=>e.startsWith("config-")&&e.endsWith(".yaml")).map(e=>({name:e,path:f(t,e),mtime:c(f(t,e)).mtime})).sort((e,t)=>t.mtime.getTime()-e.mtime.getTime()),l=new Set;a.slice(i).forEach(e=>l.add(e.path)),a.forEach(e=>{e.mtime.getTime()<s&&l.add(e.path)});for(const t of l){let i=!1;try{r(t),i=!0}catch(e){u.logError("ConfigManager.rotateBackups",e)}if(i){const i=this.getSecretsBackupPath(t);if(e(i))try{r(i)}catch(e){u.logError("ConfigManager.rotateBackups.secrets",e)}}}}catch(e){u.logError("ConfigManager.rotateBackups",e)}}saveConfig(e){try{this.ensureConfigDir(),this.backupConfig();const t=this.syncLegacyFields(this.migrateOldConfig(e||this.config)),i=this.prepareForPersistence(t),o=m.dump(i);this.atomicWrite(this.configPath,o),this.config=t}catch(e){throw u.error("Failed to save config:",e),u.logError("ConfigManager.saveConfig",e),e}}prepareForPersistence(e){const t={...e,credential_store:_};return delete t.api_key,delete t.base_url,delete t.models,t}mergeLegacySecrets(e,t){const i={...e.profiles||{}},o=new Set([...Object.keys(t.api_keys||{}),...Object.keys(t.tool_api_keys||{})]);for(const e of o){const o=i[e]||{},s=o.api_key||t.api_keys?.[e],r={...t.tool_api_keys?.[e]||{},...o.tool_api_keys||{}};i[e]={...o,...s?{api_key:s}:{},...Object.keys(r).length>0?{tool_api_keys:r}:{}}}return this.syncLegacyFields({...e,profiles:i})}getConfig(){return this.syncLegacyFields({...this.config})}updateConfig(e){this.saveConfig({...this.config,...e})}isFirstRun(){return!e(this.configPath)}getLang(){return this.config.lang||"en_US"}setLang(e){this.updateConfig({lang:e})}getActiveProfileName(){return this.config.active_profile||"default"}listProfiles(){return Object.keys(this.config.profiles||{}).sort()}getProfile(e=this.getActiveProfileName()){const t=this.config.profiles?.[e]||{};return{...t.api_key?{api_key:t.api_key}:{},...t.tool_api_keys&&Object.keys(t.tool_api_keys).length>0?{tool_api_keys:this.normalizeStringRecord(t.tool_api_keys)}:{},...t.base_url?{base_url:t.base_url}:{},models:t.models?this.normalizeModels(t.models):DEFAULT_MODELS,...t.tool_models&&Object.keys(t.tool_models).length>0?{tool_models:this.normalizeToolModels(t.tool_models)}:{},...t.tool_settings&&Object.keys(t.tool_settings).length>0?{tool_settings:this.normalizeToolSettings(t.tool_settings)}:{}}}createProfile(e,t=this.getActiveProfileName()){const i=this.getProfile(t),o={...this.config.profiles||{},[e]:{...i.api_key?{api_key:i.api_key}:{},...i.base_url?{base_url:i.base_url}:{},models:this.normalizeModels(i.models)}};this.saveConfig({...this.config,profiles:o})}setActiveProfile(e){if(!this.config.profiles?.[e])throw new Error(`Profile not found: ${e}`);this.saveConfig({...this.config,active_profile:e})}updateProfile(e,t){const i=this.config.profiles?.[e]||{},o=t.models?this.normalizeModels({...i.models,...t.models}):i.models?this.normalizeModels(i.models):void 0,s={...i,...void 0!==t.api_key?{api_key:t.api_key}:{},...void 0!==t.base_url?{base_url:t.base_url}:{},...o?{models:o}:{}},r={...this.config.profiles||{},[e]:s};this.saveConfig({...this.config,profiles:r})}deleteProfile(e){const t={...this.config.profiles||{}};if(!t[e])return;const i=Object.keys(t).filter(t=>t!==e);if(0===i.length)throw new Error("At least one profile must remain");delete t[e];const o=this.getActiveProfileName()===e?i[0]:this.getActiveProfileName();this.saveConfig({...this.config,profiles:t,active_profile:o})}getApiKey(){return this.config.profiles?.[this.getActiveProfileName()]?.api_key}setApiKey(e){this.updateProfile(this.getActiveProfileName(),{api_key:e})}revokeApiKey(){const e=this.config.profiles?.[this.getActiveProfileName()]||{},{api_key:t,tool_api_keys:i,...o}=e,s={...this.config.profiles||{},[this.getActiveProfileName()]:o};this.saveConfig({...this.config,profiles:s})}getApiKeyFingerprint(){const e=this.getApiKey();return e?p("sha256").update(e).digest("hex").slice(0,12):""}getApiKeyForTool(e){const t=this.config.profiles?.[this.getActiveProfileName()];return t?.tool_api_keys?.[e]||t?.api_key||void 0}setToolApiKey(e,t){const i=this.getActiveProfileName(),o=this.config.profiles?.[i]||{},s={...this.config.profiles||{},[i]:{...o,tool_api_keys:{...o.tool_api_keys||{},[e]:t}}};this.saveConfig({...this.config,profiles:s})}clearToolApiKey(e){const t=this.getActiveProfileName(),i=this.config.profiles?.[t]||{},{[e]:o,...s}=i.tool_api_keys||{},r={...i};Object.keys(s).length>0?r.tool_api_keys=s:delete r.tool_api_keys,this.saveConfig({...this.config,profiles:{...this.config.profiles||{},[t]:r}})}listToolApiKeys(){return Object.keys(this.config.profiles?.[this.getActiveProfileName()]?.tool_api_keys||{}).sort()}getToolApiKeyFingerprint(e){const t=this.config.profiles?.[this.getActiveProfileName()]?.tool_api_keys?.[e];return t?p("sha256").update(t).digest("hex").slice(0,12):""}getModels(){const e=this.config.profiles?.[this.getActiveProfileName()]?.models;return e?this.normalizeModels(e):DEFAULT_MODELS}setModels(e){this.updateProfile(this.getActiveProfileName(),{models:this.normalizeModels(e)})}updateModels(e){this.setModels(this.normalizeModels({...this.getModels(),...e}))}getModelsForTool(e){const t=this.getModels(),i=this.config.profiles?.[this.getActiveProfileName()]?.tool_models?.[e];return i?this.normalizeModels({...t,...i}):t}setToolModel(e,t){const i=this.getActiveProfileName(),o=this.config.profiles?.[i]||{},s={...o.tool_models||{}};t&&0!==Object.keys(t).length?s[e]=t:delete s[e];const r={...o};Object.keys(s).length>0?r.tool_models=s:delete r.tool_models;const n={...this.config.profiles||{},[i]:r};this.saveConfig({...this.config,profiles:n})}getToolSettings(e){const t=this.config.profiles?.[this.getActiveProfileName()]?.tool_settings?.[e];return t?{...t}:{}}getToolSetting(e,t){return this.config.profiles?.[this.getActiveProfileName()]?.tool_settings?.[e]?.[t]}setToolSetting(e,t,i){if(this.getToolSetting(e,t)===i)return;const o=this.getActiveProfileName(),s=this.config.profiles?.[o]||{},r={...s.tool_settings||{}};r[e]={...r[e]||{},[t]:i};const n={...this.config.profiles||{},[o]:{...s,tool_settings:r}};this.saveConfig({...this.config,profiles:n})}getCustomPresets(){return{...this.config.presets||{}}}saveCustomPreset(e,t){const i={...this.config.presets||{},[e]:{models:this.normalizeModels(t),updated_at:(new Date).toISOString()}};this.saveConfig({...this.config,presets:i})}deleteCustomPreset(e){const t={...this.config.presets||{}};t[e]&&(delete t[e],this.saveConfig({...this.config,presets:t}))}listBackups(){const t=this.getBackupDir();return e(t)?n(t).filter(e=>e.startsWith("config-")&&e.endsWith(".yaml")).map(e=>{const i=f(t,e);return{name:e,path:i,mtimeMs:c(i).mtimeMs}}).sort((e,t)=>t.mtimeMs-e.mtimeMs):[]}createBackup(s){this.ensureConfigDir();const r=this.getBackupDir();e(r)||t(r,{recursive:!0});const n=(new Date).toISOString().replace(/[:.]/g,"-"),a=this.sanitizeBackupLabel(s),c=f(r,a?`config-${n}-${a}.yaml`:`config-${n}.yaml`),l=e(this.configPath)?i(this.configPath,"utf-8"):m.dump(this.prepareForPersistence(this.getConfig()));return o(c,l,{encoding:"utf-8",mode:384}),this.rotateBackups(r,10,30),c}restoreBackup(t){if(!t||"."===t||".."===t||/[/\\]|\.\./.test(t))throw new Error(`Invalid backup name: ${t}`);const o=this.getBackupDir(),s=f(o,t);if(!e(s))throw new Error(`Backup not found: ${t}`);const r=i(s,"utf-8"),n=m.load(r),a=this.getSecretsBackupPath(s);let c=this.migrateOldConfig(n||this.createDefaultConfig());if(n?.credential_store!==_&&e(a))try{c=this.mergeLegacySecrets(c,d(a))}catch(e){throw new Error(`Legacy backup credentials could not be decrypted: ${a}`,{cause:e})}this.saveConfig(c)}getBaseUrl(){return this.runtimeBaseUrl||this.config.profiles?.[this.getActiveProfileName()]?.base_url||DEFAULT_BASE_URL}setBaseUrl(e,t=!1){this.runtimeBaseUrl=e,t&&this.updateProfile(this.getActiveProfileName(),{base_url:e})}resetBaseUrl(){this.runtimeBaseUrl=void 0;const e=this.config.profiles?.[this.getActiveProfileName()]||{},{base_url:t,...i}=e,o={...this.config.profiles||{},[this.getActiveProfileName()]:i};this.saveConfig({...this.config,profiles:o})}}export const configManager=ConfigManager.getInstance();
|
package/dist/lib/tool-manager.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execSync as o}from"child_process";import{i18n as n}from"./i18n.js";import t from"inquirer";import l from"chalk";import e from"terminal-link";import s from"ora";import{logger as i}from"../utils/logger.js";import{configManager as a}from"./config.js";import{getAdapter as r,listAdapters as c,TOOL_ADAPTERS as d}from"./tools/registry.js";import{listBackups as g,restoreBackup as m}from"./tools/backup.js";function u(o){const n=d[o];return{name:n.name,command:n.command,installCommand:n.installCommand,configPath:n.configPath,displayName:n.displayName,...n.hidden?{hidden:!0}:{}}}export const SUPPORTED_TOOLS=Object.fromEntries(Object.keys(d).map(o=>[o,u(o)]));export class ToolManager{static instance;constructor(){}static getInstance(){return ToolManager.instance||(ToolManager.instance=new ToolManager),ToolManager.instance}isToolInstalled(n){const t=SUPPORTED_TOOLS[n];if(!t)return!1;try{const n="win32"===process.platform?`where ${t.command}`:`which ${t.command}`;return o(n,{stdio:"pipe"}),!0}catch{return!1}}async installTool(a){const r=SUPPORTED_TOOLS[a];if(!r)throw new Error(`Unknown tool: ${a}`);const c=s(n.t("wizard.installing_tool")).start();try{c.info(l.gray("$ ")+l.white(r.installCommand)),c.stop(),o(r.installCommand,{stdio:"inherit"}),c.succeed(n.t("wizard.tool_installed"))}catch(s){c.fail(n.t("wizard.install_failed"));const d=(s.message||"")+(s.stderr?.toString()||"")+(s.stdout?.toString()||"");if(!(d.includes("EACCES")||d.includes("permission denied")||d.includes("EPERM")||243===s.status))throw new Error(`Failed to install ${r.displayName}: ${s}`);if(console.log("\n⚠️ "+n.t("install.permission_detected")+"\n"),"win32"===process.platform)try{const t=r.installCommand.replace("npm install -g","npm install -g --force");return console.log("🔧 "+n.t("install.trying_solution",{num:"1",desc:n.t("install.using_force")})),console.log(l.gray("$ ")+l.white(t)),o(t,{stdio:"inherit"}),void console.log("\n✅ "+n.t("install.permission_fixed"))}catch(o){i.warn("Retry install failed:",o),console.log("\n❌ "+n.t("install.auto_fix_failed")),console.log(l.yellow("\n📌 "+n.t("install.windows_solutions"))),console.log(""),console.log(l.cyan.bold(n.t("install.windows_solution_1_title"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step1"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step2"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step3"))),console.log(l.white(` ${r.installCommand}`)),console.log(""),console.log(l.cyan.bold(n.t("install.windows_solution_2_title"))),console.log(l.gray(" "+n.t("install.windows_solution_2_command"))),console.log(l.white(` ${r.installCommand.replace("npm install -g","npm install -g --prefix=%APPDATA%\\npm")}`)),console.log("");const{action:e}=await t.prompt([{type:"list",name:"action",message:n.t("install.what_next"),choices:[{name:n.t("install.installed_continue"),value:"continue"},{name:n.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===e)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(a))throw console.log(l.red("\n❌ "+n.t("install.still_not_installed",{tool:r.displayName}))),new Error(`${r.displayName} is not installed`);return void console.log(l.green("\n✅ "+n.t("install.verified_success",{tool:r.displayName})))}console.log(l.yellow("\n📌 "+n.t("install.unix_solutions"))),console.log(""),console.log(l.cyan.bold(n.t("install.unix_solution_1_title"))),console.log(l.gray(" "+n.t("install.unix_solution_1_desc"))),console.log(l.white(` sudo ${r.installCommand}`)),console.log("");const g="https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally",m=e(g,g,{fallback:()=>g});console.log(l.cyan.bold(n.t("install.unix_solution_2_title"))),console.log(l.gray(" "+n.t("install.unix_solution_2_desc"))),console.log(l.blue(" 📖 "+n.t("install.npm_docs_link")+": ")+m),console.log("");const{action:u}=await t.prompt([{type:"list",name:"action",message:n.t("install.what_next"),choices:[{name:n.t("install.installed_continue"),value:"continue"},{name:n.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===u)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(a))throw console.log(l.red("\n❌ "+n.t("install.still_not_installed",{tool:r.displayName}))),new Error(`${r.displayName} is not installed`);console.log(l.green("\n✅ "+n.t("install.verified_success",{tool:r.displayName})))}}getToolConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);try{return n.getConfig()}catch(n){return i.warn(`Failed to read config for ${o}:`,n),i.logError(`ToolManager.getToolConfig(${o})`,n),null}}updateToolConfig(o,n){const t=r(o);if(!t)throw new Error(`Unknown tool: ${o}`);if(!t.updateConfig)throw new Error(`Tool ${o} does not support raw config updates`);t.updateConfig(n)}loadPrivateConfig(o,n,t){const l=r(o);if(!l)throw new Error(`Unknown tool: ${o}`);l.loadPrivateConfig({apiKey:n,baseUrl:a.getBaseUrl(),models:t})}unloadPrivateConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);n.unloadPrivateConfig()}clearPinnedModels(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return a.setToolModel(o,null),n.clearPinnedModels?n.clearPinnedModels():[]}supportsClearPinnedModels(o){return Boolean(r(o)?.clearPinnedModels)}detectCurrentConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return n.detectCurrentConfig()}backupPathsFor(o){return o.backupPaths?o.backupPaths():[o.configPath]}listToolBackups(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(n).map(o=>({file:o,backups:g(o)}))}restorePrivateConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(n).map(o=>({file:o,result:m(o)}))}getInstalledTools(){return Object.keys(SUPPORTED_TOOLS).filter(o=>this.isToolInstalled(o))}getSupportedTools(){return c().filter(o=>!o.hidden).map(o=>u(o.name))}isGitInstalled(){try{const n="win32"===process.platform?"where git":"which git";return o(n,{stdio:"pipe"}),!0}catch{return!1}}}export const toolManager=ToolManager.getInstance();
|
|
1
|
+
import{execSync as o}from"child_process";import{i18n as n}from"./i18n.js";import t from"inquirer";import l from"chalk";import e from"terminal-link";import s from"ora";import{logger as i}from"../utils/logger.js";import{configManager as a}from"./config.js";import{getAdapter as r,listAdapters as c,TOOL_ADAPTERS as d}from"./tools/registry.js";import{listBackups as g,restoreBackup as m}from"./tools/backup.js";function u(o){const n=d[o];return{name:n.name,command:n.command,installCommand:n.installCommand,configPath:n.configPath,displayName:n.displayName,...n.hidden?{hidden:!0}:{}}}export const SUPPORTED_TOOLS=Object.fromEntries(Object.keys(d).map(o=>[o,u(o)]));export class ToolManager{static instance;constructor(){}static getInstance(){return ToolManager.instance||(ToolManager.instance=new ToolManager),ToolManager.instance}isToolInstalled(n){const t=SUPPORTED_TOOLS[n];if(!t)return!1;try{const n="win32"===process.platform?`where ${t.command}`:`which ${t.command}`;return o(n,{stdio:"pipe"}),!0}catch{return!1}}async installTool(a){const r=SUPPORTED_TOOLS[a];if(!r)throw new Error(`Unknown tool: ${a}`);const c=s(n.t("wizard.installing_tool")).start();try{c.info(l.gray("$ ")+l.white(r.installCommand)),c.stop(),o(r.installCommand,{stdio:"inherit"}),c.succeed(n.t("wizard.tool_installed"))}catch(s){c.fail(n.t("wizard.install_failed"));const d=(s.message||"")+(s.stderr?.toString()||"")+(s.stdout?.toString()||"");if(!(d.includes("EACCES")||d.includes("permission denied")||d.includes("EPERM")||243===s.status))throw new Error(`Failed to install ${r.displayName}: ${s}`);if(console.log("\n⚠️ "+n.t("install.permission_detected")+"\n"),"win32"===process.platform)try{const t=r.installCommand.replace("npm install -g","npm install -g --force");return console.log("🔧 "+n.t("install.trying_solution",{num:"1",desc:n.t("install.using_force")})),console.log(l.gray("$ ")+l.white(t)),o(t,{stdio:"inherit"}),void console.log("\n✅ "+n.t("install.permission_fixed"))}catch(o){i.warn("Retry install failed:",o),console.log("\n❌ "+n.t("install.auto_fix_failed")),console.log(l.yellow("\n📌 "+n.t("install.windows_solutions"))),console.log(""),console.log(l.cyan.bold(n.t("install.windows_solution_1_title"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step1"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step2"))),console.log(l.gray(" "+n.t("install.windows_solution_1_step3"))),console.log(l.white(` ${r.installCommand}`)),console.log(""),console.log(l.cyan.bold(n.t("install.windows_solution_2_title"))),console.log(l.gray(" "+n.t("install.windows_solution_2_command"))),console.log(l.white(` ${r.installCommand.replace("npm install -g","npm install -g --prefix=%APPDATA%\\npm")}`)),console.log("");const{action:e}=await t.prompt([{type:"list",name:"action",message:n.t("install.what_next"),choices:[{name:n.t("install.installed_continue"),value:"continue"},{name:n.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===e)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(a))throw console.log(l.red("\n❌ "+n.t("install.still_not_installed",{tool:r.displayName}))),new Error(`${r.displayName} is not installed`);return void console.log(l.green("\n✅ "+n.t("install.verified_success",{tool:r.displayName})))}console.log(l.yellow("\n📌 "+n.t("install.unix_solutions"))),console.log(""),console.log(l.cyan.bold(n.t("install.unix_solution_1_title"))),console.log(l.gray(" "+n.t("install.unix_solution_1_desc"))),console.log(l.white(` sudo ${r.installCommand}`)),console.log("");const g="https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally",m=e(g,g,{fallback:()=>g});console.log(l.cyan.bold(n.t("install.unix_solution_2_title"))),console.log(l.gray(" "+n.t("install.unix_solution_2_desc"))),console.log(l.blue(" 📖 "+n.t("install.npm_docs_link")+": ")+m),console.log("");const{action:u}=await t.prompt([{type:"list",name:"action",message:n.t("install.what_next"),choices:[{name:n.t("install.installed_continue"),value:"continue"},{name:n.t("install.cancel_install"),value:"cancel"}]}]);if("cancel"===u)throw new Error(n.t("install.user_cancelled"));if(!this.isToolInstalled(a))throw console.log(l.red("\n❌ "+n.t("install.still_not_installed",{tool:r.displayName}))),new Error(`${r.displayName} is not installed`);console.log(l.green("\n✅ "+n.t("install.verified_success",{tool:r.displayName})))}}getToolConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);try{return n.getConfig()}catch(n){return i.warn(`Failed to read config for ${o}:`,n),i.logError(`ToolManager.getToolConfig(${o})`,n),null}}updateToolConfig(o,n){const t=r(o);if(!t)throw new Error(`Unknown tool: ${o}`);if(!t.updateConfig)throw new Error(`Tool ${o} does not support raw config updates`);t.updateConfig(n)}loadPrivateConfig(o,n,t){const l=r(o);if(!l)throw new Error(`Unknown tool: ${o}`);l.loadPrivateConfig({apiKey:n,baseUrl:a.getBaseUrl(),models:t,settings:a.getToolSettings(o)})}unloadPrivateConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);n.unloadPrivateConfig()}clearPinnedModels(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return a.setToolModel(o,null),n.clearPinnedModels?n.clearPinnedModels():[]}supportsClearPinnedModels(o){return Boolean(r(o)?.clearPinnedModels)}detectCurrentConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return n.detectCurrentConfig()}backupPathsFor(o){return o.backupPaths?o.backupPaths():[o.configPath]}listToolBackups(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(n).map(o=>({file:o,backups:g(o)}))}restorePrivateConfig(o){const n=r(o);if(!n)throw new Error(`Unknown tool: ${o}`);return this.backupPathsFor(n).map(o=>({file:o,result:m(o)}))}getInstalledTools(){return Object.keys(SUPPORTED_TOOLS).filter(o=>this.isToolInstalled(o))}getSupportedTools(){return c().filter(o=>!o.hidden).map(o=>u(o.name))}isGitInstalled(){try{const n="win32"===process.platform?"where git":"which git";return o(n,{stdio:"pipe"}),!0}catch{return!1}}}export const toolManager=ToolManager.getInstance();
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import { type ToolAdapter } from './types.js';
|
|
2
|
+
export declare const CODEX_ONE_MILLION_CONTEXT_SETTING = "one_million_context";
|
|
3
|
+
/** Update only Codex context-window fields. Credentials and provider settings are untouched. */
|
|
4
|
+
export declare function setCodexOneMillionContext(enabled: boolean): boolean;
|
|
2
5
|
export declare const codexAdapter: ToolAdapter;
|
package/dist/lib/tools/codex.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as
|
|
1
|
+
import{existsSync as t,readFileSync as o}from"fs";import{join as e}from"path";import*as r from"@iarna/toml";import{logger as n}from"../../utils/logger.js";import{backupBeforeWrite as i,atomicWriteFile as c}from"./backup.js";import{pickModel as l,toolHome as s,withV1 as a}from"./types.js";import{getToolDefaultModel as m}from"../model-registry.js";const d=()=>e(s(),".codex"),u=()=>e(d(),"config.toml"),_=()=>e(d(),"auth.json"),f="OPENAI_API_KEY",p="custom",g=1e6,x=9e5;export const CODEX_ONE_MILLION_CONTEXT_SETTING="one_million_context";function y(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function h(t,o){return y(t[o])||(t[o]={}),t[o]}function v(){if(!t(u()))return{};const e=o(u(),"utf-8");if(!e.trim())return{};try{const t=r.parse(e);if(!y(t))throw new Error("config.toml root must be a TOML table");return t}catch(t){throw new Error(`Existing ~/.codex/config.toml cannot be parsed, back it up and fix it first: ${t instanceof Error?t.message:String(t)}`)}}function w(){if(!t(_()))return{};try{const t=JSON.parse(o(_(),"utf-8"));return y(t)?t:{}}catch{return{}}}function C(t,o){if(o){const o=t.model_context_window!==g||t.model_auto_compact_token_limit!==x;return t.model_context_window=g,t.model_auto_compact_token_limit=x,o}const e="model_context_window"in t||"model_auto_compact_token_limit"in t;return delete t.model_context_window,delete t.model_auto_compact_token_limit,e}export function setCodexOneMillionContext(t){const o=v();return!!C(o,t)&&(i(u()),c(u(),r.stringify(o)),!0)}export const codexAdapter={name:"codex",displayName:"Codex",command:"codex",installCommand:"npm install -g @openai/codex",configPath:u(),loadPrivateConfig(t){const o=m("codex"),e=l(t.models,o),n=function(t){return/(?:^|\/)claude(?:-|$)/i.test(t)}(e)?o:e,s=a(t.baseUrl),d=v();d.model=n,d.model_provider=p,d.model_reasoning_effort="xhigh",C(d,!1!==t.settings?.one_million_context);const g=h(d,"model_providers"),x=y(g[p])?{...g[p]}:{};delete x.env_key,g[p]={...x,name:"custom",wire_api:"responses",requires_openai_auth:!0,base_url:s};const E=h(d,"profiles"),b=y(E[n])?{...E[n]}:{};E[n]={...b,model_provider:p,model:n},h(h(d,"tui"),"model_availability_nux")[n]=4;const k=h(d,"features");k.goals=!0,k.js_repl=!1,i(u()),c(u(),r.stringify(d)),function(t){const o=t.trim();if(!o)return;const e={...w(),auth_mode:"apikey",[f]:o};i(_()),c(_(),JSON.stringify(e,null,2)+"\n")}(t.apiKey)},unloadPrivateConfig(){if(!t(u()))return;let o;try{o=v()}catch(t){return void n.logError("codexAdapter.unloadPrivateConfig",t)}const e=o.model_providers;y(e)&&(delete e[p],0===Object.keys(e).length&&delete o.model_providers),o.model_provider===p&&delete o.model_provider,i(u()),c(u(),r.stringify(o))},detectCurrentConfig(){const t=w()[f];return{apiKey:"string"==typeof t&&t.trim()?t.trim():null}},getConfig(){try{return v()}catch(t){return n.logError("codexAdapter.getConfig",t),null}},backupPaths:()=>[u(),_()]};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { VibecliModels } from '../config.js';
|
|
1
|
+
import type { VibecliModels, VibecliToolSettings } from '../config.js';
|
|
2
2
|
/**
|
|
3
3
|
* Home directory the tool adapters write under. Honors `VIBECLI_TEST_HOME` (the
|
|
4
4
|
* same override the config layer uses) so tests can redirect ~/.codex, ~/.hermes,
|
|
@@ -14,6 +14,7 @@ export interface ToolApplyContext {
|
|
|
14
14
|
apiKey: string;
|
|
15
15
|
baseUrl: string;
|
|
16
16
|
models: VibecliModels;
|
|
17
|
+
settings?: VibecliToolSettings;
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
20
|
* One self-contained integration for a downstream coding tool.
|
package/dist/lib/wizard.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"inquirer";import t from"chalk";import a from"ora";import{configManager as o,DEFAULT_MODELS as i}from"./config.js";import{getPresets as n,resolvePreset as r,getToolDefaultModel as s}from"./model-registry.js";import{toolManager as l,SUPPORTED_TOOLS as d}from"./tool-manager.js";import{i18n as c,detectSystemLang as p}from"./i18n.js";import{createBorderLine as m,createContentLine as u}from"../utils/string-width.js";import{validateApiKey as g,fetchModels as _}from"./api-validator.js";import{readFileSync as w}from"fs";import{dirname as f,join as y}from"path";import{fileURLToPath as h}from"url";const z=f(h(import.meta.url)),v=t.hex("#FF8700");export class Wizard{static instance;BOX_WIDTH=63;validatedApiKeys=new Set;constructor(){}static getInstance(){return Wizard.instance||(Wizard.instance=new Wizard),Wizard.instance}createBox(e){console.log(v.bold("\n"+m("╔","╗","═",this.BOX_WIDTH))),console.log(v.bold(u(e,"║","║",this.BOX_WIDTH,"center"))),console.log(v.bold(m("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(c.t("wizard.hint_navigate")),t.gray(c.t("wizard.hint_confirm"))];console.log(t.gray("💡 ")+e.join(t.gray(" | "))+"\n")}async promptWithHints(t){this.showOperationHints();const a=t.map(e=>"list"!==e.type||e.pageSize?e:{...e,pageSize:20});return e.prompt(a)}printBanner(){const e=65,t=c.t("wizard.banner_subtitle"),a=u(t,"║","║",e,"center"),o=u("","║","║",e,"center"),i=u(`API Helper v${function(){try{return JSON.parse(w(y(z,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[" ▄▀▄ █▀▄ █ █▄█ ██▀ █ █▀▄ ██▀ █▀▄ "," █▀█ █▀ █ █ █ █▄▄ █▄▄ █▀ █▄▄ █▀▄ "].map(t=>u(t,"║","║",e,"center")),r=[m("╔","╗","═",e),o,...n,o,i,a,m("╚","╝","═",e)];console.log(v.bold("\n"+r.join("\n")))}resetScreen(){console.clear(),this.printBanner()}printGoodbye(){const e=l.getSupportedTools().filter(e=>{try{return Boolean(l.detectCurrentConfig(e.name).apiKey)}catch{return!1}});let a;if(e.length>0){const t=e.map(e=>e.command).join(" / ");a=c.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(a+=" "+c.t("wizard.goodbye_claude_hint"))}else a=c.t("wizard.goodbye_message");console.log(t.green("\n👋 "+a))}async runFirstTimeSetup(){const e="zh_CN"===p();e&&(o.setLang("zh_CN"),c.setLocale("zh_CN")),this.resetScreen(),console.log(v.bold("\n"+c.t("wizard.welcome"))),console.log(t.gray(c.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(c.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await this.configApiKey()&&await this.selectAndConfigureTool(!0)}async configLanguage(){for(;;){this.resetScreen(),this.createBox(c.t("wizard.select_language"));const a=c.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+c.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===a?t.green(" ✓ ("+c.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===a?t.green(" ✓ ("+c.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"}],default:"zh_CN"}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;return o.setLang(i),void c.setLocale(i)}}buildPresetChoices(){const a=n(),o=a.filter(e=>e.recommended),i=a.filter(e=>!e.recommended),r=e=>({name:(c.t(e.name)!==e.name?c.t(e.name):e.name)+(e.models?t.gray(` [H:${e.models.haiku} S:${e.models.sonnet} O:${e.models.opus}]`):""),value:{type:"preset",preset:e}}),s=[];return o.length>0&&(s.push(new e.Separator(t.hex("#FF8700")("── "+c.t("wizard.recommended")+" ──"))),s.push(...o.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+c.t("wizard.other_models")+" ──"))),s.push(...i.map(r))),s}async handleCustomModelInput(){const a=o.getModels(),{customMode:n}=await e.prompt([{type:"list",name:"customMode",message:c.t("wizard.custom_model_mode"),choices:[{name:c.t("wizard.custom_model_single"),value:"single"},{name:c.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:c.t("wizard.input_custom_model"),default:i.sonnet}]);o.setModels({haiku:t,sonnet:t,opus:t,reasoning:t})}else{const t=await e.prompt([{type:"input",name:"haiku",message:c.t("wizard.model_haiku"),default:a.haiku},{type:"input",name:"sonnet",message:c.t("wizard.model_sonnet"),default:a.sonnet},{type:"input",name:"opus",message:c.t("wizard.model_opus"),default:a.opus},{type:"input",name:"reasoning",message:c.t("wizard.model_reasoning"),default:a.reasoning}]);o.setModels(t)}console.log(t.green("\n✓ "+c.t("wizard.models_saved")))}async configModels(a){const i=o.getApiKey();if(await this.validatedKeyForConfiguration(i))for(;;){this.resetScreen(),this.createBox(c.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+c.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+c.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+c.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:c.t("wizard.select_model_config"),choices:i}]);if("exit"===n.type)this.printGoodbye(),process.exit(0);else{if("back"===n.type)return;if("custom_input"===n.type)return await this.handleCustomModelInput(),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig());if("preset"===n.type){const e=r(n.preset);return o.setModels(e),console.log(t.green("\n✓ "+c.t("wizard.models_saved"))),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}else await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:c.t("wizard.load_config_now"),choices:[{name:c.t("wizard.load_config_yes"),value:!0},{name:c.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configApiKey(){for(;;){this.resetScreen(),this.createBox(c.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+c.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+c.t("wizard.api_key_get_hint",{url:"https://www.vibeapi.cn/console/token"}))),console.log();const{apiKey:i}=await e.prompt([{type:"password",name:"apiKey",message:c.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:c.t("wizard.select_action"),choices:[{name:c.t("wizard.retry_input"),value:"retry"},{name:"<- "+c.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return!1;continue}const n=a(c.t("wizard.validating_api_key")).start(),r=await g(i.trim());if(n.stop(),r.valid)return o.setApiKey(i.trim()),this.validatedApiKeys.add(i.trim()),console.log(t.green("\n✓ "+c.t("wizard.api_key_valid"))),!0;{this.validatedApiKeys.delete(i.trim()),"invalid_api_key"===r.error?console.log(t.red("\n✗ "+c.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+c.t("wizard.api_key_network_error")));const{action:a}=await e.prompt([{type:"list",name:"action",message:c.t("wizard.select_action"),choices:[{name:c.t("wizard.retry_input"),value:"retry"},{name:"<- "+c.t("wizard.nav_return"),value:"back"}]}]);if("back"===a)return!1}}}async selectAndConfigureTool(t=!1){for(;;){this.resetScreen(),this.createBox(c.t("wizard.select_tool"));const a=l.getSupportedTools().map(e=>({name:e.displayName,value:e.name})),{tool:o}=await this.promptWithHints([{type:"list",name:"tool",message:"🛠️ "+c.t("wizard.select_tool"),choices:[...a,new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===o)this.printGoodbye(),process.exit(0);else if("back"===o)return;await this.configureTool(o,t)}}async configureTool(a,o=!1){const i=d[a];if(i){if(!l.isToolInstalled(a)){console.log(t.yellow("\n"+c.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:o}=await e.prompt([{type:"confirm",name:"install",message:c.t("wizard.install_tool_confirm"),default:!0}]);if(!o)return;try{await l.installTool(a)}catch{return void console.log(t.red(c.t("wizard.install_failed")))}}o?await this.configureToolModel(a)&&await this.loadConfig(a):await this.showToolMenu(a)}}async showToolMenu(t){for(;;){this.resetScreen();const a=d[t];if(!a)return;this.createBox(a.displayName+" "+c.t("wizard.menu_title"));const o=[{name:"📥 "+c.t("wizard.action_load_config",{tool:a.displayName}),value:"load"},{name:"📤 "+c.t("wizard.action_unload_config",{tool:a.displayName}),value:"unload"}];l.supportsClearPinnedModels(t)&&o.push({name:"🧹 "+c.t("wizard.action_clear_model",{tool:a.displayName}),value:"clear-model"}),o.push({name:"↩️ "+c.t("wizard.action_restore_config",{tool:a.displayName}),value:"restore"},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"});const{action:i}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+c.t("wizard.select_action"),choices:o}]);if("exit"===i)this.printGoodbye(),process.exit(0);else{if("back"===i)return;"load"===i?await this.configureToolModel(t)&&await this.loadConfig(t):"unload"===i?await this.unloadConfig(t):"clear-model"===i?await this.clearPinnedModels(t):"restore"===i&&await this.restoreConfig(t)}}}singleModelOverride(e){const t=e.trim();return/claude/i.test(t)?{opus:t}:{haiku:t,sonnet:t,opus:t,reasoning:t}}hasConfiguredApiKey(){return!!o.getApiKey()?.trim()||o.listToolApiKeys().some(e=>Boolean(o.getApiKeyForTool(e)?.trim()))}async validatedKeyForConfiguration(e){const o=e?.trim();if(!o)return console.log(t.red("\n"+c.t("wizard.api_key_required"))),console.log(t.yellow(c.t("wizard.please_config_api_key_first"))),null;if(this.validatedApiKeys.has(o))return o;const i=a(c.t("wizard.validating_api_key")).start(),n=await g(o);if(n.valid)return this.validatedApiKeys.add(o),i.succeed(c.t("wizard.api_key_valid")),o;this.validatedApiKeys.delete(o);const r="invalid_api_key"===n.error?c.t("wizard.api_key_invalid"):c.t("wizard.api_key_network_error");return i.fail(r),console.log(t.red("\n"+c.t("wizard.please_reconfig_api_key"))),null}async configureToolModel(i){const n=d[i];if(!n)return!1;const l=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!l)return await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}]),!1;const p="claude-code"===i,m=s(i),u=p||!m?c.t("wizard.model_builtin_default"):m;this.resetScreen(),this.createBox(n.displayName+" · "+c.t("wizard.config_models"));const g=[{name:"✅ "+c.t("wizard.use_default_model",{model:u}),value:{type:"default"}},{name:"🌐 "+c.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+c.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:{type:"back"}}],{choice:w}=await this.promptWithHints([{type:"list",name:"choice",message:c.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===w.type)return!1;if("default"===w.type)o.setToolModel(i,null);else if("preset"===w.type)o.setToolModel(i,r(w.preset));else if("custom"===w.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:c.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]),a=(t||"").trim();o.setToolModel(i,a?this.singleModelOverride(a):null)}else if("fetch"===w.type){const n=a(c.t("wizard.fetching_models")).start(),r=await _(l);let s;if(n.stop(),r.ok&&r.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:c.t("wizard.select_fetched_model"),pageSize:20,choices:[...r.models,new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;s=t}else{console.log(t.yellow("\n"+c.t("wizard.fetch_models_failed")));const{model:a}=await e.prompt([{type:"input",name:"model",message:c.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]);s=(a||"").trim()}o.setToolModel(i,s?this.singleModelOverride(s):null)}return console.log(t.green("\n✓ "+c.t("wizard.models_saved"))),p&&console.log(t.gray("💡 "+c.t("wizard.claude_model_hint"))),!0}async loadConfig(i){const n=o.getModelsForTool(i),r=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!r)return void await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}]);const s=a(c.t("wizard.loading_config")).start();try{l.loadPrivateConfig(i,r,n),s.succeed(c.t("wizard.config_loaded",{tool:d[i]?.displayName||i}));const a=d[i]?.command;a&&console.log(t.green("\n"+c.t("wizard.run_tool_hint",{command:a})));const{nextAction:o}=await e.prompt([{type:"list",name:"nextAction",message:c.t("wizard.what_next"),choices:[{name:c.t("wizard.exit_and_start"),value:"exit"},{name:c.t("wizard.continue_config"),value:"continue"}]}]);"exit"===o&&(this.printGoodbye(),process.exit(0))}catch(a){s.fail(c.t("wizard.config_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async clearPinnedModels(a){const o=d[a]?.displayName||a,{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_clear_model",{tool:o}),default:!1}]);if(i){try{const e=l.clearPinnedModels(a);0===e.length?console.log(t.yellow("\n"+c.t("config.clear_model_none",{tool:o}))):(console.log(t.green("\n✓ "+c.t("config.clear_model_done",{tool:o}))),e.forEach(e=>console.log(t.gray(` - ${e}`))),console.log(t.gray(c.t("config.clear_model_restart_hint",{tool:o}))))}catch(e){console.log(t.red("\n"+(e instanceof Error?e.message:String(e))))}await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async unloadConfig(o){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_unload_config",{tool:d[o]?.displayName||o}),default:!1}]);if(!i)return;const n=a(c.t("wizard.unloading_config")).start();try{l.unloadPrivateConfig(o),n.succeed(c.t("wizard.config_unloaded"));const{nextAction:t}=await e.prompt([{type:"list",name:"nextAction",message:c.t("wizard.what_next"),choices:[{name:c.t("wizard.exit_and_start"),value:"exit"},{name:c.t("wizard.continue_config"),value:"continue"}]}]);"exit"===t&&(this.printGoodbye(),process.exit(0))}catch(a){n.fail(c.t("wizard.config_unload_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async restoreConfig(o){const i=d[o]?.displayName||o,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=a(c.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(o).filter(e=>e.result.restored);0===e.length?r.warn(c.t("wizard.restore_no_backup")):(r.succeed(c.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))))}catch(e){r.fail(c.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}async run(){await this.showMainMenu()}async showMainMenu(){if(this.hasConfiguredApiKey())for(;;){this.resetScreen();const a=o.getApiKey()||"",i=a.length>4?"sk-****"+a.slice(-4):"****";console.log(t.gray(` 🔑 API Key: ${i}`)+t.green(" ✓")),console.log("");const n=l.getSupportedTools().map(e=>({name:"🛠️ "+e.displayName,value:{type:"tool",tool:e.name}})),r=[new e.Separator(v("── "+c.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+c.t("wizard.settings")+" ──")),{name:"🔑 "+c.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🌐 "+c.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+c.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+c.t("wizard.select_tool"),choices:r}]);"exit"===s.type?(this.printGoodbye(),process.exit(0)):"tool"===s.type?await this.configureTool(s.tool):"apikey"===s.type?await this.configApiKey():"language"===s.type&&await this.configLanguage()}else await this.runFirstTimeSetup()}}export const wizard=Wizard.getInstance();
|
|
1
|
+
import e from"inquirer";import t from"chalk";import a from"ora";import{configManager as o,DEFAULT_MODELS as i}from"./config.js";import{getPresets as n,resolvePreset as r,getToolDefaultModel as s}from"./model-registry.js";import{toolManager as l,SUPPORTED_TOOLS as d}from"./tool-manager.js";import{i18n as c,detectSystemLang as p}from"./i18n.js";import{createBorderLine as m,createContentLine as u}from"../utils/string-width.js";import{validateApiKey as g,fetchModels as _}from"./api-validator.js";import{CODEX_ONE_MILLION_CONTEXT_SETTING as w}from"./tools/codex.js";import{readFileSync as f}from"fs";import{dirname as y,join as h}from"path";import{fileURLToPath as z}from"url";const v=y(z(import.meta.url)),x=t.hex("#FF8700");export class Wizard{static instance;BOX_WIDTH=63;validatedApiKeys=new Set;constructor(){}static getInstance(){return Wizard.instance||(Wizard.instance=new Wizard),Wizard.instance}createBox(e){console.log(x.bold("\n"+m("╔","╗","═",this.BOX_WIDTH))),console.log(x.bold(u(e,"║","║",this.BOX_WIDTH,"center"))),console.log(x.bold(m("╚","╝","═",this.BOX_WIDTH))),console.log("")}showOperationHints(){const e=[t.gray(c.t("wizard.hint_navigate")),t.gray(c.t("wizard.hint_confirm"))];console.log(t.gray("💡 ")+e.join(t.gray(" | "))+"\n")}async promptWithHints(t){this.showOperationHints();const a=t.map(e=>"list"!==e.type||e.pageSize?e:{...e,pageSize:20});return e.prompt(a)}printBanner(){const e=65,t=c.t("wizard.banner_subtitle"),a=u(t,"║","║",e,"center"),o=u("","║","║",e,"center"),i=u(`API Helper v${function(){try{return JSON.parse(f(h(v,"..","..","package.json"),"utf-8")).version}catch{return"0.0.0"}}()}`,"║","║",e,"center"),n=[" ▄▀▄ █▀▄ █ █▄█ ██▀ █ █▀▄ ██▀ █▀▄ "," █▀█ █▀ █ █ █ █▄▄ █▄▄ █▀ █▄▄ █▀▄ "].map(t=>u(t,"║","║",e,"center")),r=[m("╔","╗","═",e),o,...n,o,i,a,m("╚","╝","═",e)];console.log(x.bold("\n"+r.join("\n")))}resetScreen(){console.clear(),this.printBanner()}printGoodbye(){const e=l.getSupportedTools().filter(e=>{try{return Boolean(l.detectCurrentConfig(e.name).apiKey)}catch{return!1}});let a;if(e.length>0){const t=e.map(e=>e.command).join(" / ");a=c.t("wizard.goodbye_message_tools",{commands:t}),e.some(e=>"claude-code"===e.name)&&(a+=" "+c.t("wizard.goodbye_claude_hint"))}else a=c.t("wizard.goodbye_message");console.log(t.green("\n👋 "+a))}async runFirstTimeSetup(){const e="zh_CN"===p();e&&(o.setLang("zh_CN"),c.setLocale("zh_CN")),this.resetScreen(),console.log(x.bold("\n"+c.t("wizard.welcome"))),console.log(t.gray(c.t("wizard.privacy_note")+"\n")),e?console.log(t.gray(c.t("wizard.lang_auto_zh")+"\n")):await this.configLanguage(),await this.configApiKey()&&await this.selectAndConfigureTool(!0)}async configLanguage(){for(;;){this.resetScreen(),this.createBox(c.t("wizard.select_language"));const a=c.getLocale(),{language:i}=await this.promptWithHints([{type:"list",name:"language",message:"✨ "+c.t("wizard.select_language"),choices:[{name:"[EN] English"+("en_US"===a?t.green(" ✓ ("+c.t("wizard.current_active")+")"):""),value:"en_US"},{name:"[CN] 中文"+("zh_CN"===a?t.green(" ✓ ("+c.t("wizard.current_active")+")"):""),value:"zh_CN"},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"}],default:"zh_CN"}]);if("exit"===i)this.printGoodbye(),process.exit(0);else if("back"===i)return;return o.setLang(i),void c.setLocale(i)}}buildPresetChoices(){const a=n(),o=a.filter(e=>e.recommended),i=a.filter(e=>!e.recommended),r=e=>({name:(c.t(e.name)!==e.name?c.t(e.name):e.name)+(e.models?t.gray(` [H:${e.models.haiku} S:${e.models.sonnet} O:${e.models.opus}]`):""),value:{type:"preset",preset:e}}),s=[];return o.length>0&&(s.push(new e.Separator(t.hex("#FF8700")("── "+c.t("wizard.recommended")+" ──"))),s.push(...o.map(r))),i.length>0&&(s.push(new e.Separator(t.gray("── "+c.t("wizard.other_models")+" ──"))),s.push(...i.map(r))),s}async handleCustomModelInput(){const a=o.getModels(),{customMode:n}=await e.prompt([{type:"list",name:"customMode",message:c.t("wizard.custom_model_mode"),choices:[{name:c.t("wizard.custom_model_single"),value:"single"},{name:c.t("wizard.custom_model_per_role"),value:"per_role"}]}]);if("single"===n){const{customModel:t}=await e.prompt([{type:"input",name:"customModel",message:c.t("wizard.input_custom_model"),default:i.sonnet}]);o.setModels({haiku:t,sonnet:t,opus:t,reasoning:t})}else{const t=await e.prompt([{type:"input",name:"haiku",message:c.t("wizard.model_haiku"),default:a.haiku},{type:"input",name:"sonnet",message:c.t("wizard.model_sonnet"),default:a.sonnet},{type:"input",name:"opus",message:c.t("wizard.model_opus"),default:a.opus},{type:"input",name:"reasoning",message:c.t("wizard.model_reasoning"),default:a.reasoning}]);o.setModels(t)}console.log(t.green("\n✓ "+c.t("wizard.models_saved")))}async configModels(a){const i=o.getApiKey();if(await this.validatedKeyForConfiguration(i))for(;;){this.resetScreen(),this.createBox(c.t("wizard.config_models"));const i=[...this.buildPresetChoices()];i.push(new e.Separator),i.push({name:"✏️ "+c.t("wizard.custom_model_input"),value:{type:"custom_input"}}),i.push(new e.Separator),i.push({name:"<- "+c.t("wizard.nav_return"),value:{type:"back"}}),i.push({name:"x "+c.t("wizard.nav_exit"),value:{type:"exit"}});const{modelChoice:n}=await this.promptWithHints([{type:"list",name:"modelChoice",message:c.t("wizard.select_model_config"),choices:i}]);if("exit"===n.type)this.printGoodbye(),process.exit(0);else{if("back"===n.type)return;if("custom_input"===n.type)return await this.handleCustomModelInput(),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig());if("preset"===n.type){const e=r(n.preset);return o.setModels(e),console.log(t.green("\n✓ "+c.t("wizard.models_saved"))),void(a?.skipLoadPrompt||await this.promptLoadAfterModelConfig())}}}else await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}async promptLoadAfterModelConfig(){const{shouldLoad:t}=await e.prompt([{type:"list",name:"shouldLoad",message:c.t("wizard.load_config_now"),choices:[{name:c.t("wizard.load_config_yes"),value:!0},{name:c.t("wizard.load_config_later"),value:!1}]}]);t&&await this.selectAndConfigureTool()}async configApiKey(){for(;;){this.resetScreen(),this.createBox(c.t("wizard.input_api_key")),console.log(t.gray("\n💡 "+c.t("wizard.api_key_input_hint"))),console.log(t.gray(" "+c.t("wizard.api_key_get_hint",{url:"https://www.vibeapi.cn/console/token"}))),console.log();const{apiKey:i}=await e.prompt([{type:"password",name:"apiKey",message:c.t("wizard.input_your_api_key"),mask:"*"}]);if(!i||""===i.trim()){const{action:t}=await e.prompt([{type:"list",name:"action",message:c.t("wizard.select_action"),choices:[{name:c.t("wizard.retry_input"),value:"retry"},{name:"<- "+c.t("wizard.nav_return"),value:"back"}]}]);if("back"===t)return!1;continue}const n=a(c.t("wizard.validating_api_key")).start(),r=await g(i.trim());if(n.stop(),r.valid)return o.setApiKey(i.trim()),this.validatedApiKeys.add(i.trim()),console.log(t.green("\n✓ "+c.t("wizard.api_key_valid"))),!0;{this.validatedApiKeys.delete(i.trim()),"invalid_api_key"===r.error?console.log(t.red("\n✗ "+c.t("wizard.api_key_invalid"))):console.log(t.red("\n✗ "+c.t("wizard.api_key_network_error")));const{action:a}=await e.prompt([{type:"list",name:"action",message:c.t("wizard.select_action"),choices:[{name:c.t("wizard.retry_input"),value:"retry"},{name:"<- "+c.t("wizard.nav_return"),value:"back"}]}]);if("back"===a)return!1}}}async selectAndConfigureTool(t=!1){for(;;){this.resetScreen(),this.createBox(c.t("wizard.select_tool"));const a=l.getSupportedTools().map(e=>({name:e.displayName,value:e.name})),{tool:o}=await this.promptWithHints([{type:"list",name:"tool",message:"🛠️ "+c.t("wizard.select_tool"),choices:[...a,new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"}]}]);if("exit"===o)this.printGoodbye(),process.exit(0);else if("back"===o)return;await this.configureTool(o,t)}}async configureTool(a,o=!1){const i=d[a];if(i){if(!l.isToolInstalled(a)){console.log(t.yellow("\n"+c.t("wizard.tool_not_installed",{tool:i.displayName})));const{install:o}=await e.prompt([{type:"confirm",name:"install",message:c.t("wizard.install_tool_confirm"),default:!0}]);if(!o)return;try{await l.installTool(a)}catch{return void console.log(t.red(c.t("wizard.install_failed")))}}o?await this.configureToolModel(a)&&await this.loadConfig(a):await this.showToolMenu(a)}}async showToolMenu(t){for(;;){this.resetScreen();const a=d[t];if(!a)return;this.createBox(a.displayName+" "+c.t("wizard.menu_title"));const o=[{name:"📥 "+c.t("wizard.action_load_config",{tool:a.displayName}),value:"load"},{name:"📤 "+c.t("wizard.action_unload_config",{tool:a.displayName}),value:"unload"}];l.supportsClearPinnedModels(t)&&o.push({name:"🧹 "+c.t("wizard.action_clear_model",{tool:a.displayName}),value:"clear-model"}),o.push({name:"↩️ "+c.t("wizard.action_restore_config",{tool:a.displayName}),value:"restore"},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"back"},{name:"x "+c.t("wizard.nav_exit"),value:"exit"});const{action:i}=await this.promptWithHints([{type:"list",name:"action",message:"⚙️ "+c.t("wizard.select_action"),choices:o}]);if("exit"===i)this.printGoodbye(),process.exit(0);else{if("back"===i)return;"load"===i?await this.configureToolModel(t)&&await this.loadConfig(t):"unload"===i?await this.unloadConfig(t):"clear-model"===i?await this.clearPinnedModels(t):"restore"===i&&await this.restoreConfig(t)}}}singleModelOverride(e){const t=e.trim();return/claude/i.test(t)?{opus:t}:{haiku:t,sonnet:t,opus:t,reasoning:t}}hasConfiguredApiKey(){return!!o.getApiKey()?.trim()||o.listToolApiKeys().some(e=>Boolean(o.getApiKeyForTool(e)?.trim()))}async validatedKeyForConfiguration(e){const o=e?.trim();if(!o)return console.log(t.red("\n"+c.t("wizard.api_key_required"))),console.log(t.yellow(c.t("wizard.please_config_api_key_first"))),null;if(this.validatedApiKeys.has(o))return o;const i=a(c.t("wizard.validating_api_key")).start(),n=await g(o);if(n.valid)return this.validatedApiKeys.add(o),i.succeed(c.t("wizard.api_key_valid")),o;this.validatedApiKeys.delete(o);const r="invalid_api_key"===n.error?c.t("wizard.api_key_invalid"):c.t("wizard.api_key_network_error");return i.fail(r),console.log(t.red("\n"+c.t("wizard.please_reconfig_api_key"))),null}async configureToolModel(i){const n=d[i];if(!n)return!1;const l=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!l)return await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}]),!1;const p="claude-code"===i,m=s(i),u=p||!m?c.t("wizard.model_builtin_default"):m;this.resetScreen(),this.createBox(n.displayName+" · "+c.t("wizard.config_models"));const g=[{name:"✅ "+c.t("wizard.use_default_model",{model:u}),value:{type:"default"}},{name:"🌐 "+c.t("wizard.fetch_models_from_api"),value:{type:"fetch"}},...this.buildPresetChoices(),new e.Separator,{name:"✏️ "+c.t("wizard.input_custom_model"),value:{type:"custom"}},new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:{type:"back"}}],{choice:f}=await this.promptWithHints([{type:"list",name:"choice",message:c.t("wizard.select_tool_model",{tool:n.displayName}),choices:g}]);if("back"===f.type)return!1;if("default"===f.type)o.setToolModel(i,null);else if("preset"===f.type)o.setToolModel(i,r(f.preset));else if("custom"===f.type){const{model:t}=await e.prompt([{type:"input",name:"model",message:c.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]),a=(t||"").trim();o.setToolModel(i,a?this.singleModelOverride(a):null)}else if("fetch"===f.type){const n=a(c.t("wizard.fetching_models")).start(),r=await _(l);let s;if(n.stop(),r.ok&&r.models.length>0){const{model:t}=await e.prompt([{type:"list",name:"model",message:c.t("wizard.select_fetched_model"),pageSize:20,choices:[...r.models,new e.Separator,{name:"<- "+c.t("wizard.nav_return"),value:"__back__"}]}]);if("__back__"===t)return!1;s=t}else{console.log(t.yellow("\n"+c.t("wizard.fetch_models_failed")));const{model:a}=await e.prompt([{type:"input",name:"model",message:c.t("wizard.input_custom_model"),default:!p&&m?m:void 0}]);s=(a||"").trim()}o.setToolModel(i,s?this.singleModelOverride(s):null)}if("codex"===i){const t=o.getToolSetting(i,w),{enableOneMillionContext:a}=await e.prompt([{type:"confirm",name:"enableOneMillionContext",message:c.t("wizard.codex_enable_1m_context"),default:!1!==t}]);o.setToolSetting(i,w,Boolean(a))}return console.log(t.green("\n✓ "+c.t("wizard.models_saved"))),p&&console.log(t.gray("💡 "+c.t("wizard.claude_model_hint"))),!0}async loadConfig(i){const n=o.getModelsForTool(i),r=await this.validatedKeyForConfiguration(o.getApiKeyForTool(i));if(!r)return void await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}]);const s=a(c.t("wizard.loading_config")).start();try{l.loadPrivateConfig(i,r,n),s.succeed(c.t("wizard.config_loaded",{tool:d[i]?.displayName||i}));const a=d[i]?.command;a&&console.log(t.green("\n"+c.t("wizard.run_tool_hint",{command:a})));const{nextAction:o}=await e.prompt([{type:"list",name:"nextAction",message:c.t("wizard.what_next"),choices:[{name:c.t("wizard.exit_and_start"),value:"exit"},{name:c.t("wizard.continue_config"),value:"continue"}]}]);"exit"===o&&(this.printGoodbye(),process.exit(0))}catch(a){s.fail(c.t("wizard.config_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async clearPinnedModels(a){const o=d[a]?.displayName||a,{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_clear_model",{tool:o}),default:!1}]);if(i){try{const e=l.clearPinnedModels(a);0===e.length?console.log(t.yellow("\n"+c.t("config.clear_model_none",{tool:o}))):(console.log(t.green("\n✓ "+c.t("config.clear_model_done",{tool:o}))),e.forEach(e=>console.log(t.gray(` - ${e}`))),console.log(t.gray(c.t("config.clear_model_restart_hint",{tool:o}))))}catch(e){console.log(t.red("\n"+(e instanceof Error?e.message:String(e))))}await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async unloadConfig(o){const{confirm:i}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_unload_config",{tool:d[o]?.displayName||o}),default:!1}]);if(!i)return;const n=a(c.t("wizard.unloading_config")).start();try{l.unloadPrivateConfig(o),n.succeed(c.t("wizard.config_unloaded"));const{nextAction:t}=await e.prompt([{type:"list",name:"nextAction",message:c.t("wizard.what_next"),choices:[{name:c.t("wizard.exit_and_start"),value:"exit"},{name:c.t("wizard.continue_config"),value:"continue"}]}]);"exit"===t&&(this.printGoodbye(),process.exit(0))}catch(a){n.fail(c.t("wizard.config_unload_failed")),console.log(t.red(a instanceof Error?a.message:String(a))),await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}}async restoreConfig(o){const i=d[o]?.displayName||o,{confirm:n}=await e.prompt([{type:"confirm",name:"confirm",message:c.t("wizard.confirm_restore_config",{tool:i}),default:!1}]);if(!n)return;const r=a(c.t("wizard.restoring_config")).start();try{const e=l.restorePrivateConfig(o).filter(e=>e.result.restored);0===e.length?r.warn(c.t("wizard.restore_no_backup")):(r.succeed(c.t("wizard.config_restored",{tool:i})),e.forEach(e=>console.log(t.gray(` ${e.file} ← ${e.result.from}`))))}catch(e){r.fail(c.t("wizard.config_restore_failed")),console.log(t.red(e instanceof Error?e.message:String(e)))}await e.prompt([{type:"input",name:"continue",message:c.t("wizard.press_enter")}])}async run(){await this.showMainMenu()}async showMainMenu(){if(this.hasConfiguredApiKey())for(;;){this.resetScreen();const a=o.getApiKey()||"",i=a.length>4?"sk-****"+a.slice(-4):"****";console.log(t.gray(` 🔑 API Key: ${i}`)+t.green(" ✓")),console.log("");const n=l.getSupportedTools().map(e=>({name:"🛠️ "+e.displayName,value:{type:"tool",tool:e.name}})),r=[new e.Separator(x("── "+c.t("wizard.select_tool")+" ──")),...n,new e.Separator(t.gray("── "+c.t("wizard.settings")+" ──")),{name:"🔑 "+c.t("wizard.menu_change_api_key"),value:{type:"apikey"}},{name:"🌐 "+c.t("wizard.menu_config_language"),value:{type:"language"}},new e.Separator,{name:"x "+c.t("wizard.nav_exit"),value:{type:"exit"}}],{action:s}=await this.promptWithHints([{type:"list",name:"action",message:"🛠️ "+c.t("wizard.select_tool"),choices:r}]);"exit"===s.type?(this.printGoodbye(),process.exit(0)):"tool"===s.type?await this.configureTool(s.tool):"apikey"===s.type?await this.configApiKey():"language"===s.type&&await this.configLanguage()}else await this.runFirstTimeSetup()}}export const wizard=Wizard.getInstance();
|
package/dist/locales/en_US.json
CHANGED
|
@@ -145,6 +145,7 @@
|
|
|
145
145
|
"fetch_models_failed": "Failed to fetch models, please enter a model ID manually",
|
|
146
146
|
"select_fetched_model": "Select a model",
|
|
147
147
|
"claude_model_hint": "Tip: inside Claude Code you can switch models anytime with the /model command",
|
|
148
|
+
"codex_enable_1m_context": "Enable the 1M context window for Codex?",
|
|
148
149
|
"action_restore_config": "↩️ Restore {{tool}}'s last config",
|
|
149
150
|
"confirm_restore_config": "Restore {{tool}} to before the last change? (the current state is backed up first, so this is undoable)",
|
|
150
151
|
"restoring_config": "Restoring config...",
|
|
@@ -312,7 +313,9 @@
|
|
|
312
313
|
"non_interactive_aborted": "Aborted - missing required input"
|
|
313
314
|
},
|
|
314
315
|
"setup": {
|
|
315
|
-
"usage": "Usage: vibecli setup --key sk-xxx or vibecli setup --claudekey sk-a --codexkey sk-b",
|
|
316
|
+
"usage": "Usage: vibecli setup --key sk-xxx or vibecli setup --claudekey sk-a --codexkey sk-b or vibecli setup --1m",
|
|
317
|
+
"codex_1m_enabled": "Codex 1M context is enabled",
|
|
318
|
+
"codex_1m_missing_key": "No saved Codex API key was found; configure one with --key or --codexkey first",
|
|
316
319
|
"validating": "Validating API key for {{label}}...",
|
|
317
320
|
"invalid_key": "API key for {{label}} is invalid: {{reason}}",
|
|
318
321
|
"probe_failed": "Capability probe for {{label}} failed (network); continuing as if all families are available",
|
package/dist/locales/zh_CN.json
CHANGED
|
@@ -145,6 +145,7 @@
|
|
|
145
145
|
"fetch_models_failed": "获取模型列表失败,请手动输入模型 ID",
|
|
146
146
|
"select_fetched_model": "选择一个模型",
|
|
147
147
|
"claude_model_hint": "提示:在 Claude Code 里也可以随时用 /model 命令切换模型",
|
|
148
|
+
"codex_enable_1m_context": "为 Codex 启用 1M 上下文窗口?",
|
|
148
149
|
"action_restore_config": "↩️ 还原 {{tool}} 最近一次配置",
|
|
149
150
|
"confirm_restore_config": "确定把 {{tool}} 还原到上次更改前吗?(当前状态会先自动备份,可再还原回来)",
|
|
150
151
|
"restoring_config": "正在还原配置...",
|
|
@@ -312,7 +313,9 @@
|
|
|
312
313
|
"non_interactive_aborted": "已中止 - 缺少必要输入"
|
|
313
314
|
},
|
|
314
315
|
"setup": {
|
|
315
|
-
"usage": "用法: vibecli setup --key sk-xxx 或 vibecli setup --claudekey sk-a --codexkey sk-b",
|
|
316
|
+
"usage": "用法: vibecli setup --key sk-xxx 或 vibecli setup --claudekey sk-a --codexkey sk-b 或 vibecli setup --1m",
|
|
317
|
+
"codex_1m_enabled": "Codex 1M 上下文已开启",
|
|
318
|
+
"codex_1m_missing_key": "未找到已保存的 Codex API Key;请先用 --key 或 --codexkey 完成配置",
|
|
316
319
|
"validating": "正在验证 {{label}} 的 API Key...",
|
|
317
320
|
"invalid_key": "{{label}} 的 API Key 无效: {{reason}}",
|
|
318
321
|
"probe_failed": "{{label}} 的模型能力探测失败(网络原因),按全部可用继续",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeapi/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.38",
|
|
4
4
|
"description": "CLI tool for managing AI coding assistants (Claude Code, Codex, OpenClaw, Hermes, AtomCode) with one unified config",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"chalk": "^5.3.0",
|
|
51
51
|
"commander": "^12.1.0",
|
|
52
52
|
"inquirer": "^9.2.12",
|
|
53
|
-
"js-yaml": "^4.1
|
|
53
|
+
"js-yaml": "^4.3.1",
|
|
54
54
|
"ora": "^8.0.1",
|
|
55
55
|
"terminal-link": "^5.0.0"
|
|
56
56
|
},
|