@vibeapi/api-helper 0.0.88 → 0.0.90

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.
@@ -14,14 +14,10 @@ export declare function getShellRcFilePath(): string | null;
14
14
  * 返回 rc 文件是否被修改。
15
15
  */
16
16
  export declare function cleanupShellRcEnvVars(): boolean;
17
- /** 测试注入假的 PowerShell。 */
18
- export declare function setPowerShellRunnerForTests(fake: (script: string) => string): void;
19
- /** 经可注入的 PowerShell 执行一段脚本(测试里由 `setPowerShellRunnerForTests` 替换)。 */
20
- export declare function runPowerShell(script: string): string;
21
17
  /**
22
18
  * Windows 上这些变量的持久来源是「用户环境变量」「系统环境变量」和 PowerShell 配置文件里的
23
- * `$env:NAME = ...`。用户级用 .NET 接口删除(会广播变更,新开的终端立即生效,不需要管理员);
24
- * 系统级需要管理员,删不掉的名字返回给调用方提示用户。
19
+ * `$env:NAME = ...`。两级都用 reg.exe 删除(见 windows-env.ts,不等广播,避免超时);用户级不需要管理员,
20
+ * 系统级没有管理员权限时删不掉,返回给调用方提示用户。
25
21
  */
26
22
  export declare function cleanupWindowsEnvVars(names: readonly string[]): {
27
23
  machineLeft: string[];
@@ -1 +1 @@
1
- import{execFileSync as e}from"child_process";import{existsSync as n,readFileSync as r,writeFileSync as o}from"fs";import{join as t}from"path";import{logger as i}from"../utils/logger.js";import{toolHome as s}from"./tools/types.js";export const ANTHROPIC_ENV_VARS=["ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_DEFAULT_FABLE_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];export function getShellRcFilePath(){if("win32"===process.platform)return null;const e=s();switch((process.env.SHELL||"").split("/").pop()||""){case"bash":return t(e,".bashrc");case"zsh":return t(e,".zshrc");case"fish":return t(e,".config","fish","config.fish");default:return t(e,".profile")}}export function cleanupShellRcEnvVars(){if("win32"===process.platform)return!1;try{const e=getShellRcFilePath();if(!e||!n(e))return!1;let t=r(e,"utf-8");const s=t,l=[...ANTHROPIC_ENV_VARS.map(e=>new RegExp(String.raw`^\s*export\s+${e}=.*$`,"gm")),/^\s*#\s*Claude Code environment variables\s*$/gm];for(const e of l)t=t.replace(e,"");if(t!==s)return o(e,t,"utf-8"),i.info(`Cleaned up ANTHROPIC_* environment variables from ${e}`),!0}catch(e){i.warn("Failed to cleanup shell environment variables:",e),i.logError("envCleanup.cleanupShellRcEnvVars",e)}return!1}let l=n=>e("powershell.exe",["-NoProfile","-NonInteractive","-Command",n],{encoding:"utf-8",windowsHide:!0,timeout:2e4});export function setPowerShellRunnerForTests(e){l=e}export function runPowerShell(e){return l(e)}export function cleanupWindowsEnvVars(e){const c=[];if("win32"!==process.platform||0===e.length)return{machineLeft:c};try{const n=e.map(e=>`'${e}'`).join(","),r=l(`foreach($n in @(${n})){if($null -ne [Environment]::GetEnvironmentVariable($n,'User')){[Environment]::SetEnvironmentVariable($n,$null,'User');"user:$n"};if($null -ne [Environment]::GetEnvironmentVariable($n,'Machine')){try{[Environment]::SetEnvironmentVariable($n,$null,'Machine');"machine-removed:$n"}catch{"machine:$n"}}}`);for(const e of r.split(/\r?\n/)){const n=e.trim().match(/^machine:(\w+)$/);n&&c.push(n[1])}i.info(`Windows env cleanup: ${r.replace(/\s+/g," ").trim()}`)}catch(e){i.logError("envCleanup.cleanupWindowsEnvVars",e)}for(const l of function(){const e=s(),n=[];for(const r of[t(e,"Documents"),t(e,"OneDrive","Documents"),t(e,"OneDrive","文档")])for(const e of["PowerShell","WindowsPowerShell"])n.push(t(r,e,"Microsoft.PowerShell_profile.ps1"),t(r,e,"profile.ps1"));return n}())try{if(!n(l))continue;const t=r(l,"utf-8");let s=t;for(const n of e)s=s.replace(new RegExp(String.raw`^[ \t]*\$env:${n}[ \t]*=.*(\r?\n)?`,"gim"),"");s!==t&&(o(l,s,"utf-8"),i.info(`Removed ANTHROPIC_* assignments from ${l}`))}catch(e){i.logError("envCleanup.powerShellProfile",e)}return{machineLeft:c}}export function cleanupAnthropicEnvVars(){const e=ANTHROPIC_ENV_VARS.filter(e=>!!process.env[e]);if(0===e.length)return[];cleanupShellRcEnvVars(),c=cleanupWindowsEnvVars(e).machineLeft;for(const n of e)delete process.env[n];return[...e]}let c=[];export function machineEnvVarsLeft(){return c}
1
+ import{existsSync as e,readFileSync as n,writeFileSync as r}from"fs";import{join as o}from"path";import{logger as t}from"../utils/logger.js";import{toolHome as s}from"./tools/types.js";import{broadcastEnvChange as c,deleteWindowsEnv as i,readWindowsEnv as l}from"./windows-env.js";export const ANTHROPIC_ENV_VARS=["ANTHROPIC_API_KEY","ANTHROPIC_BASE_URL","ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_DEFAULT_FABLE_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];export function getShellRcFilePath(){if("win32"===process.platform)return null;const e=s();switch((process.env.SHELL||"").split("/").pop()||""){case"bash":return o(e,".bashrc");case"zsh":return o(e,".zshrc");case"fish":return o(e,".config","fish","config.fish");default:return o(e,".profile")}}export function cleanupShellRcEnvVars(){if("win32"===process.platform)return!1;try{const o=getShellRcFilePath();if(!o||!e(o))return!1;let s=n(o,"utf-8");const c=s,i=[...ANTHROPIC_ENV_VARS.map(e=>new RegExp(String.raw`^\s*export\s+${e}=.*$`,"gm")),/^\s*#\s*Claude Code environment variables\s*$/gm];for(const e of i)s=s.replace(e,"");if(s!==c)return r(o,s,"utf-8"),t.info(`Cleaned up ANTHROPIC_* environment variables from ${o}`),!0}catch(e){t.warn("Failed to cleanup shell environment variables:",e),t.logError("envCleanup.cleanupShellRcEnvVars",e)}return!1}export function cleanupWindowsEnvVars(f){const a=[];if("win32"!==process.platform||0===f.length)return{machineLeft:a};const p=[];for(const e of f)for(const n of["user","machine"])if(void 0!==l(n,e))try{i(n,e),p.push(`${n}:${e}`)}catch(r){"machine"===n&&a.push(e),t.logError("envCleanup.cleanupWindowsEnvVars",r)}p.length>0&&(c(),t.info(`Windows env cleanup: ${p.join(" ")}`));for(const c of function(){const e=s(),n=[];for(const r of[o(e,"Documents"),o(e,"OneDrive","Documents"),o(e,"OneDrive","文档")])for(const e of["PowerShell","WindowsPowerShell"])n.push(o(r,e,"Microsoft.PowerShell_profile.ps1"),o(r,e,"profile.ps1"));return n}())try{if(!e(c))continue;const o=n(c,"utf-8");let s=o;for(const e of f)s=s.replace(new RegExp(String.raw`^[ \t]*\$env:${e}[ \t]*=.*(\r?\n)?`,"gim"),"");s!==o&&(r(c,s,"utf-8"),t.info(`Removed ANTHROPIC_* assignments from ${c}`))}catch(e){t.logError("envCleanup.powerShellProfile",e)}return{machineLeft:a}}export function cleanupAnthropicEnvVars(){const e=ANTHROPIC_ENV_VARS.filter(e=>!!process.env[e]);if(0===e.length)return[];cleanupShellRcEnvVars(),f=cleanupWindowsEnvVars(e).machineLeft;for(const n of e)delete process.env[n];return[...e]}let f=[];export function machineEnvVarsLeft(){return f}
@@ -1 +1 @@
1
- import{chmodSync as n,existsSync as t,readFileSync as e,realpathSync as o,unlinkSync as r}from"fs";import{join as i}from"path";import{logger as s}from"../utils/logger.js";import{getShellRcFilePath as c,runPowerShell as f}from"./env-cleanup.js";import{atomicWriteFile as l,backupBeforeWrite as u}from"./tools/backup.js";import{toolHome as p}from"./tools/types.js";const a=n=>`# vibecli:${n}`;function m(){const n=p(),e=new Set,o=c();o&&e.add(o);for(const o of[".zshrc",".bashrc"]){const r=i(n,o);t(r)&&e.add(r)}return[...e]}function $(n,t,e){const o=`"${function(n){const t=p();return n.startsWith(t+"/")?`$HOME/${n.slice(t.length+1)}`:n}(t)}"`;return n.endsWith(".fish")?`test -f ${o}; and source ${o} ${a(e)}`:`[ -f ${o} ] && . ${o} ${a(e)}`}function v(n,e){const r=t(n)?o(n):n;u(r),l(r,e)}function E(n,t){return n.split("\n").filter(n=>!n.trimEnd().endsWith(a(t))).join("\n")}function d(n,t){if(/['\r\n]/.test(t))throw new Error(`${n} contains a quote or newline and cannot be written to a shell env file`)}export function writePersistentEnv(o,r,i){for(const[n,t]of Object.entries(i))d(n,t);const s=Object.entries(i).map(([n,t])=>`export ${n}='${t}'`).join("\n");u(r),l(r,`# Written by vibecli. Loaded from your shell profile.\n${s}\n`,384),n(r,384);for(const[n,t]of Object.entries(i))process.env[n]=t;if("win32"===process.platform){const n=Object.entries(i).map(([n,t])=>`[Environment]::SetEnvironmentVariable('${n}','${t}','User')`).join(";");return f(n),{rcFiles:[]}}const c=[];for(const n of m()){const i=t(n)?e(n,"utf-8"):"",s=$(n,r,o);if(i.split("\n").some(n=>n.trimEnd()===s)){c.push(n);continue}const f=E(i,o).replace(/\n*$/,"");v(n,`${f?f+"\n\n":""}${s}\n`),c.push(n)}return{rcFiles:c}}export function removePersistentEnv(n,o,i){const c=readPersistentEnvFile(o);if(t(o)&&(u(o),r(o)),"win32"===process.platform){const n=i.filter(n=>void 0!==c[n]).map(n=>`if([Environment]::GetEnvironmentVariable('${n}','User') -eq '${c[n]}'){[Environment]::SetEnvironmentVariable('${n}',$null,'User')}`).join(";");if(n)try{f(n)}catch(n){s.logError("persistentEnv.remove",n)}return}for(const o of m()){if(!t(o))continue;const r=e(o,"utf-8"),i=E(r,n);i!==r&&v(o,i)}}export function readPersistentEnvFile(n){const o={};if(!t(n))return o;for(const t of e(n,"utf-8").split("\n")){const n=t.match(/^export ([A-Z_][A-Z0-9_]*)='([^']*)'$/);n&&(o[n[1]]=n[2])}return o}
1
+ import{chmodSync as t,existsSync as n,readFileSync as o,realpathSync as r,unlinkSync as e}from"fs";import{join as s}from"path";import{logger as i}from"../utils/logger.js";import{getShellRcFilePath as c}from"./env-cleanup.js";import{broadcastEnvChange as f,deleteWindowsEnv as u,readWindowsEnv as p,setUserEnv as l}from"./windows-env.js";import{atomicWriteFile as m,backupBeforeWrite as a}from"./tools/backup.js";import{toolHome as $}from"./tools/types.js";const d=t=>`# vibecli:${t}`;function h(){const t=$(),o=new Set,r=c();r&&o.add(r);for(const r of[".zshrc",".bashrc"]){const e=s(t,r);n(e)&&o.add(e)}return[...o]}function v(t,n,o){const r=`"${function(t){const n=$();return t.startsWith(n+"/")?`$HOME/${t.slice(n.length+1)}`:t}(n)}"`;return t.endsWith(".fish")?`test -f ${r}; and source ${r} ${d(o)}`:`[ -f ${r} ] && . ${r} ${d(o)}`}function j(t,o){const e=n(t)?r(t):t;a(e),m(e,o)}function b(t,n){return t.split("\n").filter(t=>!t.trimEnd().endsWith(d(n))).join("\n")}function w(t,n){if(/['\r\n]/.test(n))throw new Error(`${t} contains a quote or newline and cannot be written to a shell env file`)}export function writePersistentEnv(r,e,s){for(const[t,n]of Object.entries(s))w(t,n);const i=Object.entries(s).map(([t,n])=>`export ${t}='${n}'`).join("\n");a(e),m(e,`# Written by vibecli. Loaded from your shell profile.\n${i}\n`,384),t(e,384);for(const[t,n]of Object.entries(s))process.env[t]=n;if("win32"===process.platform){for(const[t,n]of Object.entries(s))l(t,n);return f(),{rcFiles:[]}}const c=[];for(const t of h()){const s=n(t)?o(t,"utf-8"):"",i=v(t,e,r);if(s.split("\n").some(t=>t.trimEnd()===i)){c.push(t);continue}const f=b(s,r).replace(/\n*$/,"");j(t,`${f?f+"\n\n":""}${i}\n`),c.push(t)}return{rcFiles:c}}export function removePersistentEnv(t,r,s){const c=readPersistentEnvFile(r);if(n(r)&&(a(r),e(r)),"win32"===process.platform){let t=!1;for(const n of s)if(void 0!==c[n]&&p("user",n)===c[n])try{u("user",n),t=!0}catch(t){i.logError("persistentEnv.remove",t)}return void(t&&f())}for(const r of h()){if(!n(r))continue;const e=o(r,"utf-8"),s=b(e,t);s!==e&&j(r,s)}}export function readPersistentEnvFile(t){const r={};if(!n(t))return r;for(const n of o(t,"utf-8").split("\n")){const t=n.match(/^export ([A-Z_][A-Z0-9_]*)='([^']*)'$/);t&&(r[t[1]]=t[2])}return r}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Windows 持久环境变量(用户级 / 系统级)的读写,统一用 reg.exe 直接操作注册表。
3
+ * 不用 .NET 的 `[Environment]::SetEnvironmentVariable(..., 'User' | 'Machine')`:它写完同步广播
4
+ * WM_SETTINGCHANGE,不带 SMTO_ABORTIFHUNG,每个无响应的窗口等 1 秒,客户机器上整条命令会超时
5
+ * (`spawnSync powershell.exe ETIMEDOUT`)。改完由 `broadcastEnvChange()` 在后台广播,不等它结束。
6
+ */
7
+ export type EnvScope = 'user' | 'machine';
8
+ type RegRunner = (args: string[]) => string;
9
+ /** 测试注入假的 reg.exe 与广播(非 Windows 没有注册表);传 null 恢复默认。 */
10
+ export declare function setWindowsEnvRunnersForTests(runner: RegRunner | null, onBroadcast?: () => void): void;
11
+ /** 读注册表里的值;不存在时为 undefined(reg query 以非零退出)。 */
12
+ export declare function readWindowsEnv(scope: EnvScope, name: string): string | undefined;
13
+ export declare function setUserEnv(name: string, value: string): void;
14
+ /** 删除一个值;系统级没有管理员权限时 reg.exe 拒绝,异常交给调用方。 */
15
+ export declare function deleteWindowsEnv(scope: EnvScope, name: string): void;
16
+ /** 通知已打开的窗口(资源管理器等)环境变量变了,新开的终端才拿得到;后台进行。 */
17
+ export declare function broadcastEnvChange(): void;
18
+ export {};
@@ -0,0 +1 @@
1
+ import{execFileSync as e,spawn as n}from"child_process";import{logger as o}from"../utils/logger.js";const r={user:"HKCU\\Environment",machine:"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"},t=n=>e("reg.exe",n,{encoding:"utf-8",windowsHide:!0,timeout:15e3,stdio:["ignore","pipe","pipe"]}),i=()=>{const e=n("powershell.exe",["-NoProfile","-NonInteractive","-Command","[Environment]::SetEnvironmentVariable('VIBECLI_ENV_REFRESH',$null,'User')"],{detached:!0,stdio:"ignore",windowsHide:!0});e.on("error",e=>o.logError("windowsEnv.broadcast",e)),e.unref()};let s=t,d=i;export function setWindowsEnvRunnersForTests(e,n){s=e??t,d=e?n??(()=>{}):i}export function readWindowsEnv(e,n){let o;try{o=s(["query",r[e],"/v",n])}catch{return}const t=o.match(new RegExp(String.raw`^\s*${n}\s+REG_(?:EXPAND_)?SZ\s+(.*?)\r?$`,"im"));return t?t[1]:void 0}export function setUserEnv(e,n){s(["add",r.user,"/v",e,"/t","REG_SZ","/d",n,"/f"])}export function deleteWindowsEnv(e,n){s(["delete",r[e],"/v",n,"/f"])}export function broadcastEnvChange(){d()}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeapi/api-helper",
3
- "version": "0.0.88",
3
+ "version": "0.0.90",
4
4
  "description": "CLI tool for managing AI coding assistants (Claude Code, Codex, OpenClaw, Hermes, AtomCode, Grok Build) with one unified config",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",