@marsapi/cli 0.0.147 → 0.0.151
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/client-config.snapshot.json +12 -12
- package/components.json +157 -0
- package/dist/client-config.snapshot.json +12 -12
- package/dist/commands/components.js +1 -0
- package/dist/commands/config.js +1 -1
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/setup.js +1 -1
- package/dist/commands/upgrade.js +1 -1
- package/dist/components.json +157 -0
- package/dist/lib/app-update.js +1 -1
- package/dist/lib/brand.js +1 -1
- package/dist/lib/claude-code-manager.js +1 -1
- package/dist/lib/codex-sessions.js +1 -1
- package/dist/lib/command.js +1 -1
- package/dist/lib/components.js +1 -0
- package/dist/lib/config.js +1 -1
- package/dist/lib/dry-run.js +1 -1
- package/dist/lib/env-cleanup.js +1 -1
- package/dist/lib/gateway-addresses.js +1 -1
- package/dist/lib/i18n.js +1 -1
- package/dist/lib/model-registry.js +1 -1
- package/dist/lib/post-apply.js +1 -1
- package/dist/lib/powershell-env.js +1 -0
- package/dist/lib/program.js +1 -1
- package/dist/lib/runtime-options.js +1 -1
- package/dist/lib/tool-manager.js +1 -1
- package/dist/lib/tool-upgrade.js +1 -1
- package/dist/lib/tools/chatgpt-desktop.js +1 -0
- package/dist/lib/tools/claude-code.js +1 -1
- package/dist/lib/tools/claude-desktop-app.js +1 -1
- package/dist/lib/tools/claude-desktop-cli.js +1 -1
- package/dist/lib/tools/claude-desktop.js +1 -1
- package/dist/lib/tools/codex.js +1 -1
- package/dist/lib/tools/desktop-app-installer.js +1 -0
- package/dist/lib/tools/desktop-process.js +1 -1
- package/dist/lib/tools/elevated-reg.js +1 -1
- package/dist/lib/tools/tool-installs.js +1 -1
- package/dist/lib/windows-env.js +1 -1
- package/dist/lib/wizard.js +1 -1
- package/dist/lib/write-plan.js +1 -1
- package/dist/lib/written-values.js +1 -0
- package/dist/locales/en_US.json +175 -12
- package/dist/locales/zh_CN.json +175 -12
- package/package.json +2 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 1,
|
|
3
|
+
"about": "Components the CLI installs and upgrades (its list / install / upgrade commands). Read by src/lib/components.ts and by scripts/gen-components-doc.mjs (docs, the install scripts' README). {scripts} = the brand's installMirror.scripts, {agy} = installMirror.agy, {devtools} = the neutral CNB mirror of fnm / uv / Git for Windows.",
|
|
4
|
+
"devtools": "https://cnb.cool/lib/devtools/-/releases/latest/download",
|
|
5
|
+
"components": [
|
|
6
|
+
{
|
|
7
|
+
"id": "node",
|
|
8
|
+
"name": "Node.js",
|
|
9
|
+
"category": "runtime",
|
|
10
|
+
"aliases": ["nodejs"],
|
|
11
|
+
"platforms": ["darwin", "linux", "win32"],
|
|
12
|
+
"detect": { "command": "node", "pattern": "v(\\d+\\.\\d+\\.\\d+)" },
|
|
13
|
+
"latest": { "type": "node-lts", "url": "https://registry.npmmirror.com/-/binary/node/index.json" },
|
|
14
|
+
"sources": {
|
|
15
|
+
"all": "fnm({devtools})+ Node 镜像 https://npmmirror.com/mirrors/node"
|
|
16
|
+
},
|
|
17
|
+
"verify": { "checksum": "fnm:SHA256SUMS;Node:fnm 下载时核对官方 SHASUMS256.txt", "signature": "—" },
|
|
18
|
+
"install": { "type": "script", "script": "node", "forceEnv": { "NODE_INSTALL_FORCE": "1" } },
|
|
19
|
+
"upgrade": { "managedBy": "fnm", "migrateGlobalPackages": true },
|
|
20
|
+
"check": ["node", "--version"],
|
|
21
|
+
"requires": []
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "git",
|
|
25
|
+
"name": "Git",
|
|
26
|
+
"category": "runtime",
|
|
27
|
+
"platforms": ["darwin", "linux", "win32"],
|
|
28
|
+
"detect": { "command": "git", "pattern": "git version (\\d+\\.\\d+\\.\\d+)" },
|
|
29
|
+
"latest": { "type": "devtools", "key": "git", "platforms": ["win32"] },
|
|
30
|
+
"sources": {
|
|
31
|
+
"darwin": "苹果命令行工具(xcode-select)",
|
|
32
|
+
"linux": "系统包管理器(apt、dnf、yum、zypper、pacman、apk)",
|
|
33
|
+
"win32": "Git for Windows({devtools})"
|
|
34
|
+
},
|
|
35
|
+
"verify": {
|
|
36
|
+
"checksum": "Windows:SHA256SUMS(取自 GitHub 公布的 digest)",
|
|
37
|
+
"signature": "macOS / Linux:系统包管理器自己的签名"
|
|
38
|
+
},
|
|
39
|
+
"install": { "type": "script", "script": "git", "forceEnv": { "GIT_INSTALL_FORCE": "1" } },
|
|
40
|
+
"upgrade": { "managedBy": "git-for-windows-user" },
|
|
41
|
+
"check": ["git", "--version"],
|
|
42
|
+
"requires": []
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "uv",
|
|
46
|
+
"name": "uv",
|
|
47
|
+
"category": "runtime",
|
|
48
|
+
"aliases": ["python-uv"],
|
|
49
|
+
"platforms": ["darwin", "linux", "win32"],
|
|
50
|
+
"detect": { "command": "uv", "pattern": "uv (\\d+\\.\\d+\\.\\d+)" },
|
|
51
|
+
"latest": { "type": "devtools", "key": "uv" },
|
|
52
|
+
"sources": {
|
|
53
|
+
"all": "uv 官方程序({devtools});Python 从 npmmirror 的 python-build-standalone 镜像、包从阿里云 PyPI 镜像"
|
|
54
|
+
},
|
|
55
|
+
"verify": { "checksum": "SHA256SUMS", "signature": "—" },
|
|
56
|
+
"install": { "type": "script", "script": "uv", "forceEnv": { "UV_INSTALL_FORCE": "1" } },
|
|
57
|
+
"upgrade": { "managedBy": "uv-standalone" },
|
|
58
|
+
"check": ["uv", "--version"],
|
|
59
|
+
"requires": []
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "claude-code",
|
|
63
|
+
"name": "Claude Code",
|
|
64
|
+
"category": "cli",
|
|
65
|
+
"aliases": ["claude"],
|
|
66
|
+
"platforms": ["darwin", "linux", "win32"],
|
|
67
|
+
"adapter": "claude-code",
|
|
68
|
+
"latest": { "type": "npm", "package": "@anthropic-ai/claude-code" },
|
|
69
|
+
"sources": {
|
|
70
|
+
"all": "npmmirror 的 @anthropic-ai/claude-code-<平台>(与 downloads.claude.ai 同一文件),按官方原生布局安装({scripts}/claude-code.sh / .ps1)"
|
|
71
|
+
},
|
|
72
|
+
"verify": { "checksum": "npm sha512;连得上时再核对 Anthropic 发布清单的 sha256", "signature": "—" },
|
|
73
|
+
"install": { "type": "adapter" },
|
|
74
|
+
"check": ["claude", "--version"],
|
|
75
|
+
"requires": []
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "codex",
|
|
79
|
+
"name": "Codex",
|
|
80
|
+
"category": "cli",
|
|
81
|
+
"platforms": ["darwin", "linux", "win32"],
|
|
82
|
+
"adapter": "codex",
|
|
83
|
+
"latest": { "type": "npm", "package": "@openai/codex" },
|
|
84
|
+
"sources": {
|
|
85
|
+
"all": "npmmirror 的 @openai/codex(vendor 目录与官方 codex-package 逐文件相同),按官方原生布局安装({scripts}/codex.sh / .ps1)"
|
|
86
|
+
},
|
|
87
|
+
"verify": { "checksum": "npm sha512", "signature": "—" },
|
|
88
|
+
"install": { "type": "adapter" },
|
|
89
|
+
"check": ["codex", "--version"],
|
|
90
|
+
"requires": []
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "grok",
|
|
94
|
+
"name": "Grok Build",
|
|
95
|
+
"category": "cli",
|
|
96
|
+
"platforms": ["darwin", "linux", "win32"],
|
|
97
|
+
"adapter": "grok",
|
|
98
|
+
"latest": { "type": "npm", "package": "@xai-official/grok", "staleTag": true },
|
|
99
|
+
"sources": { "all": "npmmirror 的 @xai-official/grok-<平台>({scripts}/grok.sh / .ps1)" },
|
|
100
|
+
"verify": { "checksum": "npm sha512", "signature": "—" },
|
|
101
|
+
"install": { "type": "adapter" },
|
|
102
|
+
"check": ["grok", "--version"],
|
|
103
|
+
"requires": []
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"id": "agy",
|
|
107
|
+
"name": "Antigravity CLI",
|
|
108
|
+
"category": "cli",
|
|
109
|
+
"aliases": ["antigravity"],
|
|
110
|
+
"platforms": ["darwin", "linux", "win32"],
|
|
111
|
+
"adapter": "agy",
|
|
112
|
+
"latest": { "type": "version-file", "url": "{agy}/version.txt" },
|
|
113
|
+
"sources": { "all": "高速下载站 {agy}(每小时同步官方)" },
|
|
114
|
+
"verify": { "checksum": "官方清单的 sha512", "signature": "—" },
|
|
115
|
+
"install": { "type": "adapter" },
|
|
116
|
+
"check": ["agy", "--version"],
|
|
117
|
+
"requires": []
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "claude-desktop",
|
|
121
|
+
"name": "Claude Desktop",
|
|
122
|
+
"category": "app",
|
|
123
|
+
"platforms": ["darwin", "win32"],
|
|
124
|
+
"app": "claude-desktop",
|
|
125
|
+
"latest": { "type": "app" },
|
|
126
|
+
"sources": {
|
|
127
|
+
"darwin": "高速下载站 lib/claude-desktop(官方 dmg)",
|
|
128
|
+
"win32": "高速下载站 lib/claude-desktop(官方 MSIX)"
|
|
129
|
+
},
|
|
130
|
+
"verify": {
|
|
131
|
+
"checksum": "SHA256SUMS",
|
|
132
|
+
"signature": "macOS:Team ID Q6L2SF6YDW + 公证;Windows:Authenticode O=Anthropic, PBC"
|
|
133
|
+
},
|
|
134
|
+
"install": { "type": "app" },
|
|
135
|
+
"requires": []
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "chatgpt",
|
|
139
|
+
"name": "ChatGPT",
|
|
140
|
+
"category": "app",
|
|
141
|
+
"aliases": ["chatgpt-desktop", "codex-desktop", "codex-app"],
|
|
142
|
+
"platforms": ["darwin", "win32"],
|
|
143
|
+
"app": "chatgpt",
|
|
144
|
+
"latest": { "type": "app" },
|
|
145
|
+
"sources": {
|
|
146
|
+
"darwin": "高速下载站 lib/chatgpt-desktop(官方 dmg,只有 Apple 芯片版)",
|
|
147
|
+
"win32": "高速下载站 lib/chatgpt-desktop(Microsoft Store 同一个 MSIX)"
|
|
148
|
+
},
|
|
149
|
+
"verify": {
|
|
150
|
+
"checksum": "SHA256SUMS",
|
|
151
|
+
"signature": "macOS:Team ID 2DC432GLL2 + 公证;Windows:Microsoft Store 签名(OpenAI 发布者)"
|
|
152
|
+
},
|
|
153
|
+
"install": { "type": "app" },
|
|
154
|
+
"requires": []
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
package/dist/lib/app-update.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"chalk";import e from"inquirer";import{existsSync as r,mkdirSync as n,readFileSync as a,writeFileSync as o}from"fs";import{join as
|
|
1
|
+
import t from"chalk";import e from"inquirer";import{existsSync as r,mkdirSync as n,readFileSync as a,writeFileSync as o}from"fs";import{join as p}from"path";import{CLI_NAME as l}from"./brand.js";import{configManager as i}from"./config.js";import{i18n as s}from"./i18n.js";import{canPrompt as u,hasExplicitYes as d,isJsonMode as c}from"./runtime-options.js";import{track as f}from"./telemetry.js";import{compareVersions as m}from"./model-registry.js";import{listAdapters as g}from"./tools/registry.js";import{logger as _}from"../utils/logger.js";function y(){return p(i.getConfigDir(),"app-releases.json")}function w(){try{if(!r(y()))return{};const t=JSON.parse(a(y(),"utf-8"));return t&&"object"==typeof t?t:{}}catch{return{}}}function h(t,e){try{n(i.getConfigDir(),{recursive:!0}),o(y(),JSON.stringify({...w(),[t]:e},null,2))}catch(t){_.logError("app-update.writeCache",t)}}function v(){return g().flatMap(t=>[...t.appInstaller?[{name:t.name,displayName:t.displayName,appInstaller:t.appInstaller,installedVersion:t.installedVersion,runningApp:t.runningApp,tool:t.name}]:[],...(t.companionApps??[]).map(e=>({...e,tool:t.name}))])}export function appTools(){return v().map(t=>t.name)}export function getApp(t){return v().find(e=>e.name===t)}export function appsForTools(t){return v().filter(e=>t.includes(e.tool)).map(t=>t.name)}function k(t){const e=getApp(t);if(!e)throw new Error(`no app installer for ${t}`);return e}function b(t,e,r){try{return e()}catch(e){return _.logError(`app-update.${t}`,e),r}}function q(t){const e=k(t),r=e.appInstaller,n=b("installedPath",()=>null!==r.installedPath(),!1);return{tool:t,name:e.displayName,installed:n,current:n?b("installedVersion",()=>e.installedVersion?.()??null,null):null,running:!!n&&b("isRunning",()=>e.runningApp?.isRunning()??!1,!1),blocker:b("blocker",()=>r.blocker(),"unsupported_platform")}}function x(t,e,r){const n=Boolean(t.installed&&t.current&&e&&m(t.current,e)<0);return{...t,latest:e,outdated:n,...r?{error:r}:{}}}export async function appLatest(t,e={}){const r=w()[t],n=r&&Date.now()-r.checked_at<(r.version?36e5:6e5);if(r&&n&&!e.refresh)return{version:r.version??null,...r.error?{error:r.error}:{}};try{const e=await k(t).appInstaller.latestVersion();return h(t,{version:e,checked_at:Date.now()}),{version:e}}catch(e){const n=e instanceof Error?e.message:String(e);return _.logError(`app-update.latest(${t})`,e),h(t,{error:n,checked_at:Date.now()}),{version:r?.version??null,error:n}}}export async function appStatus(t,e={}){const r=q(t);if(r.blocker&&"classic_install"!==r.blocker)return x(r,null);const{version:n,error:a}=await appLatest(t,e);return x(r,n,a)}export function appStatusWith(t,e){return x(q(t),e.version,e.error)}export function cachedAppStatus(t){const e=q(t);return x(e,e.blocker&&"classic_install"!==e.blocker?null:w()[t]?.version??null)}export function appStatusText(t){return t.installed?t.outdated?s.t("app_update.outdated",{current:t.current,latest:t.latest}):t.current&&t.latest?s.t("app_update.up_to_date",{current:t.current}):t.current??"":t.latest?s.t("app_update.not_installed",{latest:t.latest}):""}export function appUpgradeCommand(t){return`${l} upgrade ${t}`}function C(t){c()?process.stderr.write(t+"\n"):console.log(t)}async function S(t,r=!0){const{ok:n}=await e.prompt([{type:"confirm",name:"ok",message:t,default:r}]);return n}let U={totalMs:12e4,stepMs:1e3};export function setQuitWaitForTests(t){U=t}export async function upgradeApp(e,r={}){const n=Date.now(),a=await appStatus(e,{refresh:r.refresh??!0}),o=a.name,p=(t,r={})=>{const o={...a,result:t,upgraded:"upgraded"===t||"installed"===t,version_after:null,...r};return["up_to_date","unavailable","not_installed","offered"].includes(t)||f(`app-upgrade:${e}`,"failed"===t?"fail":"ok",{duration_ms:Date.now()-n,detail:{result:t,current:a.current,latest:a.latest,...r.reason?{reason:r.reason}:{}}}),o};if(a.blocker)return!r.quiet&&a.installed&&a.outdated&&C(t.yellow(" ! "+s.t("app_update.outdated",{current:a.current,latest:a.latest}))),r.quiet||C(t.yellow(" ! "+s.t(`app_update.blocked_${a.blocker}`,{tool:o,url:k(e).appInstaller.downloadPage,command:appUpgradeCommand(e)}))),p("unavailable",{reason:a.blocker});if(!a.latest)return r.quiet||C(t.yellow(" ! "+s.t("app_update.latest_unknown",{tool:o,reason:a.error??""}))),p("unavailable",{reason:a.error});if(!a.installed&&!r.install)return p("not_installed");const l=Boolean(r.force&&a.installed&&!a.outdated);if(a.installed&&!a.outdated&&!l)return r.quiet||(C(t.green(" √ "+s.t("app_update.already_latest",{tool:o,current:a.current??a.latest}))),C(t.gray(" "+s.t("app_update.force_hint",{command:appUpgradeCommand(e)})))),p("up_to_date");if(l?C(t.yellow(" ! "+s.t("app_update.reinstalling",{tool:o,latest:a.latest,current:a.current??"?"}))):!a.installed||r.askBeforeUpgrade&&!a.running||C(t.yellow(` ! ${o} `+appStatusText(a))),a.installed&&r.askBeforeUpgrade&&!a.running){if(!u()||d())return C(t.gray(" "+s.t("app_update.upgrade_hint",{tool:o,current:a.current??"?",latest:a.latest,command:appUpgradeCommand(e)}))),p("offered");if(!await S(s.t("app_update.upgrade_offer",{tool:o,current:a.current??"?",latest:a.latest})))return C(t.gray(" "+s.t("app_update.declined_hint",{command:appUpgradeCommand(e)}))),p("declined")}let i=!1;if(a.running){if(!u()||d())return C(t.yellow(" ! "+s.t("app_update.running_hint",{tool:o,command:appUpgradeCommand(e)}))),p("left_running");if(!await S(s.t("app_update.offer_quit",{tool:o,latest:a.latest})))return C(t.gray(" "+s.t("app_update.declined_hint",{command:appUpgradeCommand(e)}))),p("declined");const r=k(e).runningApp;if(r?.quit?.())i=!0;else{if(!r?.quitRefused?.()||!await async function(e,r){C(t.yellow(" ! "+s.t("app_update.quit_confirm_in_app",{tool:e,seconds:String(Math.round(U.totalMs/1e3))})));const n=Date.now()+U.totalMs;for(;Date.now()<n;)if(await new Promise(t=>setTimeout(t,U.stepMs)),!r.isRunning())return await new Promise(t=>setTimeout(t,Math.min(1e3,U.stepMs))),!0;return!1}(o,r)){const n=r?.quitRefused?.()?"app_update.quit_refused":"app_update.quit_failed";return C(t.yellow(" ! "+s.t(n,{tool:o,command:appUpgradeCommand(e)}))),p(r?.quitRefused?.()?"left_running":"failed",{reason:r?.quitRefused?.()?"quit_refused":"quit_failed"})}i=!0}}try{const{version:r}=await async function(e){const r=k(e).appInstaller,n=!c()&&Boolean(process.stdout.isTTY);let a=-1;try{return await r.install({downloading:n=>C(t.gray(" "+s.t("app_update.downloading",{tool:k(e).displayName,version:n,size:String(r.sizeMb())}))),percent:e=>{!n||e-a<5||(a=e,process.stdout.write(t.gray(`\r ${e}%`)),e>=100&&process.stdout.write("\n"))},verifying:()=>{n&&a>=0&&a<100&&process.stdout.write("\n"),a=100,C(t.gray(" "+s.t("app_update.verifying",{vendor:r.vendor})))},installing:()=>C(t.gray(" "+s.t("app_update.installing"))),elevate:async()=>!(!u()||!d()&&!await S(s.t("app_update.elevate_offer",{tool:k(e).displayName}))||(C(t.gray(" "+s.t("app_update.elevating"))),0))})}finally{n&&a>=0&&a<100&&process.stdout.write("\n")}}(e);return C(t.green(" √ "+(l?s.t("app_update.reinstalled",{tool:o,version:r}):a.installed?s.t("app_update.upgraded",{tool:o,from:a.current??"?",to:r}):s.t("app_update.installed",{tool:o,version:r})))),i&&!k(e).runningApp?.launch?.()&&C(t.yellow(" ! "+s.t("setup.reopen_hint",{tool:o}))),p(a.installed?"upgraded":"installed",{version_after:r,current:a.current})}catch(r){const n=r instanceof Error?r.message:String(r);return _.logError(`app-update.install(${e})`,r),C(t.yellow(" ! "+(a.installed?s.t("app_update.upgrade_failed",{tool:o,reason:n,current:a.current??"?",command:appUpgradeCommand(e)}):s.t("app_update.install_failed",{tool:o,reason:n,command:appUpgradeCommand(e)})))),i&&k(e).runningApp?.launch?.(),p("failed",{reason:n})}}
|
package/dist/lib/brand.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,readdirSync as r,readFileSync as t}from"fs";import{dirname as n,join as o}from"path";import{fileURLToPath as a}from"url";const i=["id","displayName","productName","cliName","envPrefix","configDirName","providerKey","apiKeyEnv","desktopEntryName","packageName"];export const NEUTRAL_CLI_NAME="codekey";export function normalizeBrand(e){const r=e.cliName??e.shortName??"codekey";return{...e,cliName:r,envPrefix:e.envPrefix??r.toUpperCase().replace(/-/g,"_"),configDirName:e.configDirName??`.${r}`}}export function validateBrand(e,r="brand"){if(!e||"object"!=typeof e)throw new Error(`${r}: not an object`);const t=normalizeBrand(e);for(const e of i)if("string"!=typeof t[e]||!t[e].trim())throw new Error(`${r}: ${e} is required`);if(!/^[a-z][a-z0-9-]*$/.test(t.cliName))throw new Error(`${r}: cliName must be a lowercase command name`);if(!/^[A-Z][A-Z0-9_]*$/.test(t.envPrefix))throw new Error(`${r}: envPrefix must be UPPER_SNAKE`);if(!/^[a-z][a-z0-9-]*$/.test(t.providerKey))throw new Error(`${r}: providerKey must be lowercase`);const n=t.gatewayAddresses;if(!Array.isArray(n)||0===n.length||n.some(e=>!e||"string"!=typeof e.url||!/^https:\/\//.test(e.url)||"string"!=typeof e.label))throw new Error(`${r}: gatewayAddresses must be a non-empty list of { url: https://…, label }`);const o=t.links;if(!o||"string"!=typeof o.tokenPage)throw new Error(`${r}: links.tokenPage is required`);if(!Array.isArray(t.pluginMarketplaces))throw new Error(`${r}: pluginMarketplaces must be a list`);if(!Array.isArray(t.updateRegistries)||0===t.updateRegistries.length)throw new Error(`${r}: updateRegistries must be a non-empty list`);return t}export function loadBrandFile(e){return validateBrand(JSON.parse(t(e,"utf-8")),e)}let s=function(){const i=n(a(import.meta.url)),s=o(i,"..","brand.json");if(e(s))return loadBrandFile(s);const
|
|
1
|
+
import{existsSync as e,readdirSync as r,readFileSync as t}from"fs";import{dirname as n,join as o}from"path";import{fileURLToPath as a}from"url";const i=["id","displayName","productName","cliName","envPrefix","configDirName","providerKey","apiKeyEnv","desktopEntryName","packageName"];export const NEUTRAL_CLI_NAME="codekey";export function normalizeBrand(e){const r=e.cliName??e.shortName??"codekey";return{...e,cliName:r,envPrefix:e.envPrefix??r.toUpperCase().replace(/-/g,"_"),configDirName:e.configDirName??`.${r}`}}export function validateBrand(e,r="brand"){if(!e||"object"!=typeof e)throw new Error(`${r}: not an object`);const t=normalizeBrand(e);for(const e of i)if("string"!=typeof t[e]||!t[e].trim())throw new Error(`${r}: ${e} is required`);if(!/^[a-z][a-z0-9-]*$/.test(t.cliName))throw new Error(`${r}: cliName must be a lowercase command name`);if(!/^[A-Z][A-Z0-9_]*$/.test(t.envPrefix))throw new Error(`${r}: envPrefix must be UPPER_SNAKE`);if(!/^[a-z][a-z0-9-]*$/.test(t.providerKey))throw new Error(`${r}: providerKey must be lowercase`);const n=t.gatewayAddresses;if(!Array.isArray(n)||0===n.length||n.some(e=>!e||"string"!=typeof e.url||!/^https:\/\//.test(e.url)||"string"!=typeof e.label))throw new Error(`${r}: gatewayAddresses must be a non-empty list of { url: https://…, label }`);const o=t.links;if(!o||"string"!=typeof o.tokenPage)throw new Error(`${r}: links.tokenPage is required`);if(!Array.isArray(t.pluginMarketplaces))throw new Error(`${r}: pluginMarketplaces must be a list`);if(!Array.isArray(t.updateRegistries)||0===t.updateRegistries.length)throw new Error(`${r}: updateRegistries must be a non-empty list`);return t}export function loadBrandFile(e){return validateBrand(JSON.parse(t(e,"utf-8")),e)}let s=function(){const i=n(a(import.meta.url)),s=o(i,"..","brand.json");if(e(s))return loadBrandFile(s);const c=o(i,"..","..","brands");for(const n of e(c)?r(c).filter(e=>e.endsWith(".json")).sort():[]){const e=JSON.parse(t(o(c,n),"utf-8"));if(!0===e.default)return validateBrand(e,o(c,n))}throw new Error(`brand.json not found next to ${i}`)}();export function brand(){return s}export function setBrandForTests(e){const r=s;return s=e,()=>{s=r}}export function brandEnv(e){return process.env[brandEnvName(e)]}export function brandEnvName(e){return`${s.envPrefix}_${e}`}function c(e,r){return"zh_CN"===r?e.join("、"):e.length>1?`${e.slice(0,-1).join(", ")} and ${e[e.length-1]}`:e.join("")}export function brandTokens(e){const r=new URL(s.gatewayAddresses[0].url),t=s.gatewayAddresses[1]?.url??s.gatewayAddresses[0].url;return{brand:s.displayName,product:s.productName,cli:s.cliName,package:s.packageName,configDir:s.configDirName,envPrefix:s.envPrefix,siteHost:r.host,altSiteUrl:t,updateSources:c(s.updateRegistries.map(e=>e.label),e)}}export const BRAND_NAME=s.displayName;export const PROVIDER_KEY=s.providerKey;export const API_KEY_ENV=s.apiKeyEnv;export const CLI_NAME=s.cliName;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as o,renameSync as
|
|
1
|
+
import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as o,renameSync as s,unlinkSync as i}from"fs";import{join as r,dirname as a}from"path";import{toolHome as l}from"./tools/types.js";import{logger as c}from"../utils/logger.js";import{configManager as d,DEFAULT_MODELS as g}from"./config.js";import{loadSettingsTemplate as u}from"./settings-template.js";import{cleanupAnthropicEnvVars as f}from"./env-cleanup.js";import{getClaudeCodeTierChains as m,getGatewayModelRules as O,getRetiredDefaultModelIds as C}from"./model-registry.js";import{claimField as h,forgetField as p,ownsValue as _}from"./written-values.js";import{noteWrittenGatewayUrl as E}from"./gateway-addresses.js";import{autoCompactTokens as A,modelBlurb as T,prettyModelName as v}from"./gateway-models.js";import{baseModelId as P}from"./model-catalog.js";export const MODEL_ENV_VARS=["ANTHROPIC_DEFAULT_HAIKU_MODEL","ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_DEFAULT_FABLE_MODEL","ANTHROPIC_MODEL","ANTHROPIC_REASONING_MODEL"];const N="claude-code.model",S=e=>`claude-code.env.${e}`;function M(e){const t=Object.values(m()).flat(),n=[...Object.values(g),...Object.values(e)].filter(e=>"string"==typeof e&&""!==e);return[...new Set(["opus[1m]",...n,...C(),...t])]}const D={opus:"ANTHROPIC_DEFAULT_OPUS_MODEL",sonnet:"ANTHROPIC_DEFAULT_SONNET_MODEL",haiku:"ANTHROPIC_DEFAULT_HAIKU_MODEL",fable:"ANTHROPIC_DEFAULT_FABLE_MODEL"},L="CLAUDE_CODE_MAX_CONTEXT_TOKENS";function y(){return r(d.getConfigDir(),"claude-code-picker.json")}function I(){try{const e=JSON.parse(n(y(),"utf-8")).ids;return Array.isArray(e)?e.map(String):[]}catch{return[]}}function R(e,t=[]){if(!e||"object"!=typeof e)return!1;const{options:n,replaceBuiltInOptions:o}=e;if(!Array.isArray(n)||o)return!1;const s=O(),i=new Set([...s.models.map(e=>e.id),...s.retired,...I(),...t]);return n.every(e=>e&&"object"==typeof e&&i.has(P(String(e.model))))}export function applyGatewayModels(e,n){if(void 0===n)return e;const s={...e};if(void 0!==s.modelPicker&&!R(s.modelPicker,n.map(e=>e.id)))return s;const i=R(s.modelPicker)?s.modelPicker.options.map(e=>P(e.model)):[],r={...s.env||{}},a={...s.modelSettings||{}};for(const e of i){if(!a[e])continue;const{autoCompactWindow:t,...n}=a[e];Object.keys(n).length>0?a[e]=n:delete a[e]}const l=e=>(e.contextWindow??0)>=1e6,g=n.filter(e=>!l(e)).map(e=>e.contextWindow).filter(e=>"number"==typeof e&&e>0);!function(e){try{t(d.getConfigDir(),{recursive:!0}),o(y(),JSON.stringify({ids:e})+"\n")}catch(e){c.logError("claudeCode.recordWrittenPickerIds",e)}}(n.map(e=>e.id));const u=e=>T(e.description,{millionContext:l(e)});if(0===n.length)delete s.modelPicker,delete r[L];else{s.modelPicker={options:n.map(e=>({model:l(e)?`${e.id}[1m]`:e.id,label:e.name||v(e.id),...u(e)?{description:u(e)}:{}}))},g.length>0?r[L]=String(Math.min(...g)):delete r[L];for(const e of n){if(!e.contextWindow)continue;const t=A(e.contextWindow,e.autoCompactTokens);a[e.id]={...a[e.id],autoCompactWindow:Math.min(Math.max(t,1e5),1e6)}}}return Object.keys(a).length>0?s.modelSettings=a:delete s.modelSettings,Object.keys(r).length>0?s.env=r:delete s.env,s}export function resolveTierFallbacks(e){if(!e||0===e.length)return[];const t=new Set(e.map(P)),n=[];for(const[e,o]of Object.entries(m())){const s=D[e],[i,...r]=o;if(!s||!i||t.has(i))continue;const a=r.find(e=>t.has(e));a&&n.push({tier:e,envVar:s,from:i,to:a})}return n}export class ClaudeCodeManager{static instance;settingsPath;mcpConfigPath;lastTierFallbacks=[];lastKept=[];constructor(){this.settingsPath=r(l(),".claude","settings.json"),this.mcpConfigPath=r(l(),".claude.json")}static getInstance(){return ClaudeCodeManager.instance||(ClaudeCodeManager.instance=new ClaudeCodeManager),ClaudeCodeManager.instance}ensureConfigDir(n){const o=a(n);e(o)||t(o,{recursive:!0})}atomicWrite(t,n){const r=`${t}.tmp`;try{o(r,n,"utf-8"),s(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,n,o){this.ensureOnboardingCompleted(),f();const s=u(),i=s.env||{},{env:r,...a}=s,l=this.getSettings(),{ANTHROPIC_API_KEY:c,...m}=l.env||{},O={...m},C={},A=e=>{const n=(t[e]||"").trim();return n&&n!==g[e]?n:null},T=A("haiku"),v=A("sonnet"),P=A("opus"),D=new Set([T&&"haiku",v&&"sonnet",P&&"opus"].filter(Boolean)),L=resolveTierFallbacks(n).filter(e=>!D.has(e.tier));for(const e of L)C[e.envVar]=e.to;const y=new Set;T&&(C.ANTHROPIC_DEFAULT_HAIKU_MODEL=T),v&&(C.ANTHROPIC_DEFAULT_SONNET_MODEL=v),P&&(C.ANTHROPIC_DEFAULT_OPUS_MODEL=P,C.ANTHROPIC_MODEL=P),T&&y.add("ANTHROPIC_DEFAULT_HAIKU_MODEL"),v&&y.add("ANTHROPIC_DEFAULT_SONNET_MODEL"),P&&["ANTHROPIC_DEFAULT_OPUS_MODEL","ANTHROPIC_MODEL"].forEach(e=>y.add(e));const I=M(t);this.lastKept=[];for(const e of MODEL_ENV_VARS){const t=O[e],n=C[e];if(void 0!==n)h(S(e),t,n,{explicit:y.has(e),legacy:I})?O[e]=n:this.lastKept.push(`${e}=${t}`);else{if(void 0===t)continue;_(S(e),t,I)?(delete O[e],p(S(e))):this.lastKept.push(`${e}=${t}`)}}this.lastTierFallbacks=L.filter(e=>O[e.envVar]===e.to);const R={...l};for(const[e,t]of Object.entries(a))"model"!==e&&(e in R||(R[e]=t));R.showThinkingSummaries=!0;const U={...R,env:{...O,...Object.fromEntries(Object.entries(i).filter(([e])=>!(e in O))),ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_BASE_URL:d.getBaseUrl()}};E(d.getBaseUrl());const H=P??("string"==typeof a.model?a.model:void 0),F=l.model;void 0===H?void 0!==F&&_(N,F,I)&&(delete U.model,p(N)):h(N,F,H,{explicit:Boolean(P),legacy:I})?U.model=H:this.lastKept.push(`model=${String(F)}`),this.saveSettings(applyGatewayModels(U,o))}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(),t=M(d.getModelsForTool("claude-code")),n=void 0!==e.model&&_(N,e.model,t);if(p(N),!e.env){if(n){const{model:t,...n}=e;this.saveSettings(n)}return}const{ANTHROPIC_AUTH_TOKEN:o,ANTHROPIC_BASE_URL:s,API_TIMEOUT_MS:i,CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:r,CLAUDE_CODE_AUTO_MODE_SERVER:a,...l}=e.env,c={...l};for(const e of MODEL_ENV_VARS)e in c&&_(S(e),c[e],t)&&delete c[e],p(S(e));const g={...applyGatewayModels({...e,env:c},[])};g.env=g.env||{},n&&delete g.model,g.env&&0===Object.keys(g.env).length&&delete g.env,this.saveSettings(g)}clearPinnedModels(e){const t=this.getSettings(),n=t.env,o=n?.ANTHROPIC_AUTH_TOKEN?Object.fromEntries(resolveTierFallbacks(e).map(e=>[e.envVar,e.to])):{},s=[];for(const e of MODEL_ENV_VARS)n&&e in n&&n[e]!==o[e]&&s.push(e);void 0!==t.model&&s.push("model");const i=Object.entries(o).some(([e,t])=>n?.[e]!==t);if(0===s.length&&!i)return[];const r={...t};if(delete r.model,n){const e={...n};for(const t of MODEL_ENV_VARS)delete e[t];Object.assign(e,o),0===Object.keys(e).length?delete r.env:r.env=e}this.saveSettings(r),p(N);for(const e of MODEL_ENV_VARS)o[e]?h(S(e),o[e],o[e]):p(S(e));return s}getLastKept(){return[...this.lastKept]}getLastTierFallbacks(){return[...this.lastTierFallbacks]}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(),o=n.env||{},s={...n,env:{...o,[e]:t}};this.saveSettings(s)}removeExperimentalFeature(e){const t=this.getSettings();if(!t.env)return;const{[e]:n,...o}=t.env,s={...t,env:o};this.saveSettings(s)}}export const claudeCodeManager=ClaudeCodeManager.getInstance();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{spawnSync as t}from"child_process";import{closeSync as e,copyFileSync as o,createReadStream as r,createWriteStream as n,existsSync as s,mkdirSync as i,openSync as a,readFileSync as c,readSync as
|
|
1
|
+
import{spawnSync as t}from"child_process";import{closeSync as e,copyFileSync as o,createReadStream as r,createWriteStream as n,existsSync as s,mkdirSync as i,openSync as a,readFileSync as c,readSync as l,readdirSync as f,realpathSync as d,renameSync as p,statSync as u,utimesSync as m,writeFileSync as h}from"fs";import{basename as g,join as b,resolve as y}from"path";import{pipeline as S}from"stream/promises";import*as _ from"@iarna/toml";import{codexHome as v,toolHome as w}from"./tools/types.js";import{CLI_NAME as E}from"./brand.js";import{powerShellEnv as x}from"./powershell-env.js";export{v as codexHome};export function currentCodexProvider(){try{const t=_.parse(c(b(v(),"config.toml"),"utf-8"));return"string"==typeof t.model_provider&&t.model_provider.trim()?t.model_provider.trim():"openai"}catch{return"openai"}}let N=()=>{if("win32"===process.platform)return`${t("powershell.exe",["-NoProfile","-NonInteractive","-Command","(Get-Process -Name codex -ErrorAction SilentlyContinue).Id"],{encoding:"utf-8",windowsHide:!0,env:x()}).stdout??""}`.split(/\s+/).filter(Boolean).map(Number);const e=process.getuid?.(),o=`${t("pgrep",["-x","-i",...void 0===e?[]:["-u",String(e)],"codex"],{encoding:"utf-8"}).stdout??""}`.split(/\s+/).filter(Boolean).map(Number);if("linux"!==process.platform)return o;const r=O(v());return o.filter(t=>{let e;try{e=c(`/proc/${t}/environ`,"utf-8")}catch{return!0}const o=new Map(e.split("\0").map(t=>[t.slice(0,t.indexOf("=")),t.slice(t.indexOf("=")+1)])),n=o.get("CODEX_HOME")?.trim()||(o.get("HOME")?b(o.get("HOME"),".codex"):null);return null===n||O(n)===r})};function O(t){try{return d(t)}catch{return t}}export function setCodexProcessCheckForTests(t){N=t}export function runningCodexPids(){try{return N()}catch{return[]}}function $(t){const o=a(t,"r");try{const t=[],e=Buffer.alloc(65536);for(;;){const r=l(o,e,0,e.length,null);if(r<=0)break;const n=e.subarray(0,r).indexOf(10);if(n>=0){t.push(Buffer.from(e.subarray(0,n)));break}t.push(Buffer.from(e.subarray(0,r)))}return Buffer.concat(t).toString("utf-8")}finally{e(o)}}async function D(t,e,o){const{atime:s,mtime:i}=u(t),a=`${t}.${E}-tmp`,c=n(a);c.write(e),await S(r(t,{start:o}),c),p(a,t),m(t,s,i)}export function codexSqliteHome(){const t=v();try{const e=_.parse(c(b(t,"config.toml"),"utf-8")),o="string"==typeof e.sqlite_home?e.sqlite_home.trim():"";if(o)return"~"===o||o.startsWith("~/")?b(w(),o.slice(1)):y(t,o)}catch{}const e=process.env.CODEX_SQLITE_HOME?.trim();return e?y(e):t}function T(){const t=codexSqliteHome();if(!s(t))return null;const e=f(t).map(t=>/^state_(\d+)\.sqlite$/.exec(t)).filter(t=>!!t).sort((t,e)=>Number(e[1])-Number(t[1]));return e.length>0?b(t,e[0][0]):null}const A={threads:"id",local_thread_catalog:"thread_id"};function B(){const t=b(codexSqliteHome(),"sqlite");return s(t)?f(t).filter(t=>/\.(db|sqlite3?)$/.test(t)).map(e=>b(t,e)).sort():[]}function C(t,e,o){const r=function(t,e){return new Set(t.prepare(`PRAGMA table_info(${e})`).all().map(t=>String(t.name)))}(t,e),n=A[e];if(!r.has(n)||!r.has("model_provider"))return null;const s=r.has("host_id")?" AND host_id = 'local'":"";return t.prepare(`SELECT ${n} AS id, model_provider AS "from" FROM ${e} WHERE COALESCE(model_provider, '') NOT IN ('', ?)${s}`).all(o).map(t=>({id:String(t.id),from:String(t.from)}))}async function H(t){try{return new((await import("node:sqlite")).DatabaseSync)(t)}catch(t){return`node:sqlite unavailable (Node ${process.versions.node}; needs 22.13+): ${t instanceof Error?t.message:String(t)}`}}export const NO_STATE_DB="no state DB";export async function moveSessionsToProvider(t,e={}){const r=function(){const t=[],e=o=>{if(s(o))for(const r of f(o,{withFileTypes:!0})){const n=b(o,r.name);r.isDirectory()?e(n):r.isFile()&&r.name.endsWith(".jsonl")&&t.push(n)}};return e(b(v(),"sessions")),e(b(v(),"archived_sessions")),t}(),n=[];for(const e of r){let o,r;try{o=$(e)}catch{continue}try{r=JSON.parse(o)}catch{continue}const s=r.payload?.model_provider;"session_meta"===r.type&&"string"==typeof s&&s!==t&&(r.payload.model_provider=t,n.push({file:e,from:s,line:JSON.stringify(r),length:Buffer.byteLength(o,"utf-8")}))}const a=[T(),...B()].filter(t=>!!t);let c=a.length>0?null:NO_STATE_DB;const l=[];for(const e of a){const o=await H(e);if("string"==typeof o){c=o;break}const r=[];try{for(const n of Object.keys(A)){const s=C(o,n,t);s&&s.length>0&&r.push({db:e,table:n,rows:s})}}catch{}l.push({path:e,db:o,tables:r})}const d=l.flatMap(t=>t.tables),p=new Set(d.flatMap(t=>t.rows.map(t=>t.id))),u={target:t,scanned:r.length,moved:n.map(({file:t,from:e})=>({file:t,from:e})),dbRows:p.size,dbSkipped:c,backupDir:null},m=()=>l.forEach(t=>t.db.close());if(e.dryRun||0===n.length&&0===d.length)return m(),u;const y=b(v(),"backups",`${E}-sessions-${(new Date).toISOString().replace(/[:.]/g,"-")}`);i(y,{recursive:!0}),l.forEach(({path:t,tables:e},r)=>{if(0!==e.length)for(const e of["","-wal","-shm"])s(t+e)&&o(t+e,b(y,`${r}-${g(t)}${e}`))});const S={target:t,moved:u.moved,tables:d};h(b(y,"manifest.json"),JSON.stringify(S,null,2)+"\n");for(const t of n)await D(t.file,t.line,t.length);for(const{db:e,tables:o}of l)for(const{table:r,rows:n}of o){const o=e.prepare(`UPDATE ${r} SET model_provider = ? WHERE ${A[r]} = ? AND model_provider = ?`);for(const{id:e,from:r}of n)o.run(t,e,r)}return m(),u.backupDir=y,u}export async function undoSessionMove(t){const e=JSON.parse(c(b(t,"manifest.json"),"utf-8"));let o=0;for(const{file:t,from:r}of e.moved){if(!s(t))continue;const e=$(t),n=JSON.parse(e);n.payload&&(n.payload.model_provider=r,await D(t,JSON.stringify(n),Buffer.byteLength(e,"utf-8")),o+=1)}const r=e.tables??(e.db&&e.threads?[{db:e.db,table:"threads",rows:e.threads}]:[]),n=new Set;for(const{db:t,table:o,rows:i}of r){if(!s(t)||0===i.length||!A[o])continue;const r=await H(t);if("string"==typeof r)continue;const a=r.prepare(`UPDATE ${o} SET model_provider = ? WHERE ${A[o]} = ? AND model_provider = ?`);for(const{id:t,from:o}of i)Number(a.run(o,t,e.target).changes)>0&&n.add(t);r.close()}return{files:o,dbRows:n.size}}
|
package/dist/lib/command.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i18n as
|
|
1
|
+
import{i18n as r,detectSystemLang as n}from"./i18n.js";import{configManager as o}from"./config.js";import{runModelRefresh as t}from"./model-refresh.js";import{refreshClientConfig as e}from"./client-config-sync.js";import{normalizeBaseUrl as i}from"./gateway-addresses.js";import s from"chalk";import{setRuntimeOptions as m,detectColorPreferenceFromEnv as a,isDryRun as c}from"./runtime-options.js";import{runStartupUpdate as d}from"./auto-update.js";import{buildProgram as u}from"./program.js";import{firstCommandWord as l,isHelpRequest as p,renderHelpTopic as g}from"./cli-help.js";import{reportCliError as f,usageError as h,CliError as y}from"./errors.js";import{CLI_NAME as _}from"./brand.js";import{dryRunUnsupportedCommand as w}from"./dry-run.js";import{flushTelemetry as j,installClientHeaders as k,setTelemetryCommand as v,track as C}from"./telemetry.js";export{buildProgram}from"./program.js";const x=new Set(["setup","init","auth","enter","config"]);export class Command{program;configuring=!1;constructor(){const t=o.isLangChosen()?o.getLang():n();r.loadFromConfig(t),this.program=u()}preflightArgs(r){const n=n=>r.includes(n),t=!n("--no-color")&&a();if(m({nonInteractive:n("--non-interactive"),json:n("--json"),yes:n("--yes")||n("-y"),dryRun:n("--dry-run"),color:t}),t||(s.level=0),this.configuring=!1,p(r))return;let e;for(let n=0;n<r.length;n+=1){const o=r[n];"--base"===o&&r[n+1]?(e=r[n+1],n+=1):o.startsWith("--base=")&&(e=o.slice(7))}const c=l(r),d=void 0===c&&!["--version","-v"].some(n);this.configuring=d||void 0!==c&&x.has(c),e&&o.setBaseUrl(i(e)??e,this.configuring&&!n("--dry-run"))}async execute(r){const n=Date.now();let o=!1;try{if(this.preflightArgs(r),k(),p(r)||["--version","-v"].some(n=>r.includes(n))||(v(l(r)??"wizard"),C("command","start",{detail:{args:r.filter(r=>r.startsWith("-")&&!/key/i.test(r))}}),o=!0),"help"===r[0]&&r[1]){const n=g(r[1]);if(n)return void process.stdout.write(n)}if(this.rejectUnknownCommand(r),!p(r)){if(c()&&this.rejectUnsupportedDryRun(r),(await d(r)).handled)return;this.configuring&&await e({timeoutMs:5e3}),await t(r)}if(await this.program.parseAsync(r,{from:"user"}),o){const r=Boolean(process.exitCode)&&0!==process.exitCode;C("command",r?"fail":"ok",{duration_ms:Date.now()-n,...r?{detail:{exit_code:process.exitCode}}:{}})}}catch(t){if("commander.helpDisplayed"===t?.code||"commander.version"===t?.code)return;const e=this.normalizeCommanderError(t,r),i=f(e);if(o){const r=e instanceof y?`${e.type}${e.subtype?"/"+e.subtype:""}`:void 0;C("command",130===i?"cancel":"fail",{duration_ms:Date.now()-n,error:e,detail:{exit_code:i,...r?{type:r}:{}}})}}finally{o&&await j()}}rejectUnsupportedDryRun(n){const o=w(n);if(null!==o)throw h(r.t("runtime.dry_run_unsupported",{command:o,cli:_}),r.t("runtime.dry_run_unsupported_hint",{cli:_}),"dry_run_unsupported")}rejectUnknownCommand(n){const o=l(n);if(!o||"help"===o)return;if(this.program.commands.some(r=>r.name()===o))return;const t=this.suggestCommand(o),e=t?r.t("cli.unknown_hint_suggestion",{command:t}):r.t("cli.unknown_hint");throw h(r.t("cli.error_unknown",{command:o}),e,"unknown_command")}suggestCommand(r){const n=(r,n)=>{const o=Array.from({length:n.length+1},(r,n)=>n);for(let t=1;t<=r.length;t+=1){let e=o[0];o[0]=t;for(let i=1;i<=n.length;i+=1){const s=o[i];o[i]=Math.min(o[i]+1,o[i-1]+1,e+(r[t-1]===n[i-1]?0:1)),e=s}}return o[n.length]};return this.program.commands.map(r=>r.name()).map(o=>({name:o,score:n(r,o)})).filter(({score:n})=>n<=Math.max(2,Math.floor(r.length/2))).sort((r,n)=>r.score-n.score)[0]?.name}normalizeCommanderError(n,o){if(n instanceof y)return n;const t=n?.code,e=n instanceof Error?n.message:String(n);if("commander.unknownOption"===t||"commander.missingArgument"===t||"commander.missingMandatoryOption"===t||"commander.invalidArgument"===t){const n=l(o),i=/'([^']+)'/.exec(e)?.[1]??"",s="commander.unknownOption"===t?"cli.err_unknown_option":"commander.missingArgument"===t?"cli.err_missing_argument":"commander.missingMandatoryOption"===t?"cli.err_missing_option":null,m=s&&i?r.t(s,{name:i}):e.replace(/^error:\s*/,"");return h(m,n?r.t("cli.unknown_option_hint",{command:n}):r.t("cli.unknown_option_hint_root"),t)}return n}getProgram(){return this.program}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{spawnSync as t}from"child_process";import{existsSync as e,mkdirSync as n,readFileSync as o,readdirSync as r,realpathSync as s,statSync as a,writeFileSync as i}from"fs";import{delimiter as l,dirname as c,isAbsolute as u,join as p,sep as d}from"path";import{fileURLToPath as m}from"url";import f from"chalk";import{brand as g,CLI_NAME as h}from"./brand.js";import{configManager as w}from"./config.js";import{i18n as v}from"./i18n.js";import{appLatest as y,appStatus as b,appStatusWith as $,cachedAppStatus as _,getApp as P}from"./app-update.js";import{compareVersions as x}from"./model-registry.js";import{isJsonMode as A}from"./runtime-options.js";import{track as k}from"./telemetry.js";import{runWatched as S}from"./tool-upgrade.js";import{getAdapter as D}from"./tools/registry.js";import{findInstallsIncludingNative as L,forgetInstalls as j,NPM_MIRROR as E,npmMirrorVersion as R}from"./tools/tool-installs.js";import{toolHome as C}from"./tools/types.js";import{logger as T}from"../utils/logger.js";let M=null;export function parseManifest(t){const e=JSON.parse(t);if(1!==e.schema||!Array.isArray(e.components))throw new Error("components.json: schema must be 1 with a components list");const n=new Set;for(const t of e.components){if(!t.id||!t.name||!["runtime","cli","app"].includes(t.category))throw new Error(`components.json: bad entry ${JSON.stringify(t.id)}`);for(const e of[t.id,...t.aliases??[]]){if(n.has(e))throw new Error(`components.json: duplicate name ${e}`);n.add(e)}if("runtime"===t.category&&(!t.detect||"script"!==t.install.type))throw new Error(`components.json: runtime ${t.id} needs detect + a script`);if("cli"===t.category&&!t.adapter)throw new Error(`components.json: cli ${t.id} needs an adapter`);if("app"===t.category&&!t.app)throw new Error(`components.json: app ${t.id} needs an app id`)}for(const t of e.components)for(const e of t.requires)if(!n.has(e))throw new Error(`components.json: ${t.id} requires unknown ${e}`);return e}function N(){return M||(M=parseManifest(o(function(){const t=c(m(import.meta.url)),n=[p(t,"..","..","components.json"),p(t,"..","components.json"),p(t,"components.json")],o=n.find(t=>e(t));if(!o)throw new Error(`components.json not found, searched: ${n.join(", ")}`);return o}(),"utf-8"))),M}export function setComponentManifestForTests(t){M=null===t?null:parseManifest(t)}export function listComponents(){return N().components}export function resolveComponent(t){const e=t.trim().toLowerCase();return listComponents().find(t=>t.id===e||(t.aliases??[]).includes(e))}let O=null;export function componentPlatform(){return O??process.platform}export function expandUrl(t){const e=g().installMirror??{};let n=!1;const o=t.replace(/\{(scripts|agy|devtools)\}/g,(t,o)=>{const r="devtools"===o?N().devtools:e[o];return r||(n=!0),r??""});return n?null:o}export function componentSource(t,e=componentPlatform()){return(t.sources[e]??t.sources.all??"").replace(/\{(scripts|agy|devtools)\}/g,(t,e)=>("devtools"===e?N().devtools:g().installMirror?.[e])??t)}export function installBlocker(t=process.env,e=componentPlatform()){return"win32"===e?/\\system32\\config\\systemprofile/i.test(t.USERPROFILE??"")||t.USERNAME?.endsWith("$")?"system":null:0===(U??process.getuid?.())&&t.SUDO_USER&&"root"!==t.SUDO_USER?"sudo":null}let U=null,I=!0,B=e=>{const n=/\.(cmd|bat)$/i.test(e)?t(`"${e}" --version`,{encoding:"utf-8",timeout:1e4,windowsHide:!0,shell:!0,stdio:["ignore","pipe","pipe"]}):t(e,["--version"],{encoding:"utf-8",timeout:1e4,windowsHide:!0,stdio:["ignore","pipe","pipe"]});return`${n.stdout??""}${n.stderr??""}`},F=()=>0===t("xcode-select",["-p"],{stdio:"ignore",timeout:1e4}).status;function V(t){try{return s(t)}catch{return t}}function H(t,e){return"win32"===e?[`${t}.exe`,`${t}.cmd`]:[t]}export function knownRuntimePaths(t,e=componentPlatform(),n=process.env){const o=C();if("node"===t){if("win32"===e){const t=n.FNM_DIR||p(n.APPDATA||p(o,"AppData","Roaming"),"fnm");return[p(t,"aliases","default","node.exe"),p(n.LOCALAPPDATA||p(o,"AppData","Local"),"Programs","nodejs","node.exe")]}return[p(W(e,n),"aliases","default","bin","node"),"/opt/homebrew/bin/node","/usr/local/bin/node","/usr/bin/node"]}if("uv"===t)return"win32"===e?[p(o,".local","bin","uv.exe"),p(o,".cargo","bin","uv.exe")]:[p(n.UV_INSTALL_DIR||p(o,".local","bin"),"uv"),p(o,".cargo","bin","uv"),"/opt/homebrew/bin/uv","/usr/local/bin/uv"];if("git"===t){if("win32"===e){const t=n.LOCALAPPDATA||p(o,"AppData","Local");return[p(t,"Programs","Git","cmd","git.exe"),p(n.ProgramFiles||"C:\\Program Files","Git","cmd","git.exe")]}return["/opt/homebrew/bin/git","/usr/local/bin/git","/usr/bin/git"]}return[]}function W(t,n){if(n.FNM_DIR)return n.FNM_DIR;const o=C();if("win32"===t)return p(n.APPDATA||p(o,"AppData","Roaming"),"fnm");const r=p(o,".local","share","fnm"),s=p(o,"Library","Application Support","fnm");return"darwin"===t&&!e(r)&&e(s)?s:r}let G=()=>{const e=e=>{const n=`${t("reg",["query",e,"/v","Path"],{encoding:"utf-8",timeout:1e4,windowsHide:!0,stdio:["ignore","pipe","ignore"]}).stdout??""}`.split(/\r?\n/).find(t=>/\sREG_(EXPAND_)?SZ\s/.test(t));return n?n.replace(/^.*?REG_(EXPAND_)?SZ\s+/,""):""};return[e("HKCU\\Environment"),e("HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment")].flatMap(t=>{return(e=t,e.replace(/%([^%]+)%/g,(t,e)=>process.env[e]??t)).split(";");var e}).map(t=>t.trim().replace(/^"|"$/g,"")).filter(Boolean)},J=null;export function probeRuntime(t,e=componentPlatform()){const n=t.detect,o=new RegExp(n.pattern),r=new Set,s=[],i=n=>{if(!function(t){try{return a(t).isFile()}catch{return!1}}(n))return;if("darwin"===e&&"git"===t.id&&"/usr/bin/git"===n&&!F())return;const i=V(n),l="win32"===e?i.toLowerCase():i;if(r.has(l))return;r.add(l);const c=o.exec(B(n));c&&s.push({path:n,version:c[1]??null})},c=(process.env.PATH??"").split("win32"===e?";":l);"win32"===e&&c.push(...J??=z(()=>G())??[]);for(const t of c)if(t&&u(t))for(const o of H(n.command,e))i(p(t,o));for(const n of knownRuntimePaths(t.id,e))(I||n.startsWith(C()))&&i(n);return s}export function scriptTarget(t,e=componentPlatform(),n=process.env){const o=C();return"node"===t?"win32"===e?p(W(e,n),"aliases","default","node.exe"):p(W(e,n),"aliases","default","bin","node"):"uv"===t?p(n.UV_INSTALL_DIR||p(o,".local","bin"),"win32"===e?"uv.exe":"uv"):"git"===t&&"win32"===e?p(n.LOCALAPPDATA||p(o,"AppData","Local"),"Programs","Git","cmd","git.exe"):null}export function runtimeManager(t,e,n=componentPlatform(),o=process.env){const r=V(e),s=t=>"win32"===n?t.replace(/\//g,"\\").toLowerCase():t,a=s(r),i=s(e),l="win32"===n?"\\":"\\"===d?"/":d,u=t=>a.includes(s(t))||i.includes(s(t)),m=t=>a.startsWith(s(t)+l)||i.startsWith(s(t)+l),f=C(),g=/[\\/](Cellar|Caskroom|homebrew)[\\/]/i.test(r)||/^\/opt\/homebrew\//.test(e)||/^\/usr\/local\/(Cellar|opt)\//.test(r);if("node"===t)return u(`${l}fnm${l}`)||m(W(n,o))?{by:"fnm",auto:!0}:g?{by:"brew",auto:!1,hint:"brew upgrade node"}:u(`${l}.nvm${l}`)?{by:"nvm",auto:!1,hint:"nvm install --lts --reinstall-packages-from=current"}:u(`${l}nvm${l}`)&&"win32"===n?{by:"nvm",auto:!1,hint:"nvm install lts"}:u(`${l}.volta${l}`)||u(`${l}volta${l}`)?{by:"volta",auto:!1,hint:"volta install node@lts"}:u(`${l}scoop${l}`)?{by:"scoop",auto:!1,hint:"scoop update nodejs-lts"}:u(`${l}.asdf${l}`)?{by:"asdf",auto:!1,hint:"asdf install nodejs latest"}:u(`${l}mise${l}`)?{by:"mise",auto:!1,hint:"mise use -g node@lts"}:{by:"system",auto:!1};if("uv"===t)return[o.UV_INSTALL_DIR,p(f,".local","bin"),p(f,".cargo","bin")].filter(Boolean).some(t=>s(c(e))===s(t)||s(c(r))===s(t))?{by:"standalone",auto:!0}:g?{by:"brew",auto:!1,hint:"brew upgrade uv"}:u("pipx")?{by:"pipx",auto:!1,hint:"pipx upgrade uv"}:u(`${l}scoop${l}`)?{by:"scoop",auto:!1,hint:"scoop update uv"}:u("winget")?{by:"winget",auto:!1,hint:"winget upgrade astral-sh.uv"}:u("site-packages")||u(`${l}python`)?{by:"pip",auto:!1,hint:"pip install -U uv"}:{by:"other",auto:!1};if("git"===t){if("win32"===n){const t=o.LOCALAPPDATA||p(f,"AppData","Local");return m(p(t,"Programs","Git"))?{by:"git-for-windows-user",auto:!0}:u(`${l}scoop${l}`)?{by:"scoop",auto:!1,hint:"scoop update git"}:{by:"git-for-windows",auto:!1,hint:"git update-git-for-windows"}}return g?{by:"brew",auto:!1,hint:"brew upgrade git"}:"darwin"===n?{by:"xcode",auto:!1,hint:"softwareupdate --list"}:{by:"system",auto:!1}}return{by:"other",auto:!1}}function q(){return p(w.getConfigDir(),"component-releases.json")}function K(){try{const t=JSON.parse(o(q(),"utf-8"));return t&&"object"==typeof t?t:{}}catch{return{}}}function X(t,e){try{n(w.getConfigDir(),{recursive:!0}),i(q(),JSON.stringify({...K(),[t]:e},null,2))}catch(t){T.logError("components.writeCache",t)}}let Z=async(t,e=15e3)=>{const n=await fetch(t,{signal:AbortSignal.timeout(e)});if(!n.ok)throw new Error(`HTTP ${n.status} ${t}`);return n.text()};const Y=/(\d+\.\d+\.\d+)/;export function latestNodeLts(t){const e=JSON.parse(t).find(t=>"string"==typeof t.lts&&t.lts&&"string"==typeof t.version);if(!e)throw new Error("no LTS release in the Node index");return e.version.replace(/^v/,"")}export function devtoolsVersion(t,e){const n=t.split(/\r?\n/).find(t=>t.startsWith(`${e}=`)),o=n?Y.exec(n.slice(e.length+1)):null;if(!o)throw new Error(`${e} is not in versions.txt`);return o[1]}export async function latestVersion(t,e={}){const n=componentPlatform(),o=`${t.id}:${n}`,r=K()[o],s=r&&Date.now()-r.checked_at<(r.version?36e5:6e5);if(r&&s&&!e.refresh)return{version:r.version??null,...r.error?{error:r.error}:{}};try{const e=await async function(t,e){const n=t.latest;switch(n.type){case"node-lts":return latestNodeLts(await Z(n.url));case"devtools":return n.platforms&&!n.platforms.includes(e)?null:devtoolsVersion(await Z(`${N().devtools}/versions.txt`),n.key);case"npm":{if(n.staleTag){const e=D(t.adapter??"")?.installSpec,n=e?await R(e):"latest";if("latest"!==n)return n}const e=JSON.parse(await Z(`${E}/${n.package}/latest`));if(!e.version)throw new Error(`no version for ${n.package}`);return e.version}case"version-file":{const t=expandUrl(n.url);if(!t)return null;const e=Y.exec(await Z(t));if(!e)throw new Error(`no version in ${t}`);return e[1]}default:return null}}(t,n);return e&&X(o,{version:e,checked_at:Date.now()}),{version:e}}catch(e){const n=e instanceof Error?e.message:String(e);return T.logError(`components.latest(${t.id})`,e),X(o,{error:n,checked_at:Date.now()}),{version:r?.version??null,error:n}}}export async function prefetchLatest(t,e={}){const n=componentPlatform(),o=await Promise.all(t.map(async t=>t.platforms.includes(n)?"app"===t.category?[t.id,P(t.app)?await y(t.app,e):{version:null}]:[t.id,await latestVersion(t,e)]:[t.id,{version:null}]));return new Map(o)}export async function componentStatus(t,e={}){const n=componentPlatform(),o={id:t.id,name:t.name,category:t.category,supported:t.platforms.includes(n),source:componentSource(t,n)},r={...o,installed:!1,current:null,latest:null,outdated:!1,path:null,managed_by:null,auto_upgrade:!1,blocker:o.supported?installBlocker():"unsupported_platform"};if(!o.supported)return r;if("app"===t.category){const n=P(t.app);if(!n)return{...r,blocker:"unsupported_platform"};const o=e.latest?$(t.app,e.latest):e.offline?_(t.app):await b(t.app,{refresh:e.refresh});return{...r,installed:o.installed,current:o.current,latest:o.latest,outdated:o.outdated,path:o.installed?z(()=>n.appInstaller.installedPath()):null,managed_by:o.installed?"app":null,auto_upgrade:o.installed&&!o.blocker,blocker:o.blocker??installBlocker(),...o.error?{error:o.error}:{}}}let s=null,a=null,i=null;if("cli"===t.category){const e=D(t.adapter)?.installSpec,n=e?L(e)[0]:void 0;n&&(s=n.version,a=n.path,i={by:n.kind,auto:!0})}else{const e=probeRuntime(t,n)[0];e&&(s=e.version,a=e.path,i=runtimeManager(t.id,e.path,n))}const{version:l,error:c}=e.latest?e.latest:e.offline?{version:K()[`${t.id}:${n}`]?.version??null,error:void 0}:await latestVersion(t,{refresh:e.refresh}),u=l?K()[`${t.id}:${n}:ceiling`]:void 0,p=Boolean(u?.version&&u.error===l&&s&&x(s,u.version)>=0);return{...r,installed:Boolean(a),current:s,latest:l,outdated:Boolean(s&&l&&x(s,l)<0&&!p),...p?{capped_at:u.version}:{},path:a,managed_by:i?.by??null,auto_upgrade:i?.auto??!1,...i?.hint?{hint:i.hint}:{},...c?{error:c}:{}}}function z(t){try{return t()}catch{return null}}const Q={idleMs:18e4,timeoutMs:36e5};let tt=async(t,e)=>{const n={...Q,env:e};return"ok"===("win32"===componentPlatform()?await S("powershell.exe",["-NoProfile","-ExecutionPolicy","Bypass","-Command",`irm ${t} -TimeoutSec 30 | iex`],n):await S("sh",["-c",'script=$(curl -fsSL --connect-timeout 15 --max-time 60 "$0") && printf \'%s\\n\' "$script" | sh',t],n))},et=async(t,e)=>"ok"===await S(t,e,Q);function nt(t){A()?process.stderr.write(t+"\n"):console.log(t)}export function globalPackages(t,e=componentPlatform()){const n=c(V(t)),s="win32"===e?p(n,"node_modules"):p(c(n),"lib","node_modules"),a=[],i=(t,e)=>{try{const n=JSON.parse(o(p(t,"package.json"),"utf-8"));n.version&&a.push({name:e,version:n.version})}catch{}};let l=[];try{l=r(s)}catch{return a}for(const t of l)if(!t.startsWith(".")&&"npm"!==t&&"corepack"!==t)if(t.startsWith("@"))try{for(const e of r(p(s,t)))e.startsWith(".")||i(p(s,t,e),`${t}/${e}`)}catch{}else i(p(s,t),t);return a}function ot(t,e,n={}){const o=["installed","upgraded","reinstalled","up_to_date","already_installed"].includes(e);return{tool:t.id,category:t.category,current:t.current,latest:t.latest,result:e,version_after:null,ok:o,...n}}function rt(t){return`${h} upgrade ${t}`}export async function runRuntime(t,e,n={}){const o=Date.now(),r=componentPlatform(),s=await componentStatus(t,{refresh:!0}),a=t.name;if(!s.supported)return nt(f.yellow(" ! "+v.t("components.unsupported_platform",{tool:a}))),ot(s,"unavailable",{reason:"unsupported_platform"});if(s.blocker)return nt(f.yellow(" ! "+function(t,e){return v.t(`components.blocked_${t}`,{tool:e})}(s.blocker,a))),ot(s,"unavailable",{reason:s.blocker});const i=function(t){if("script"!==t.install.type)return null;const e=g().installMirror?.scripts;return e?`${e}/${t.install.script}.${"win32"===componentPlatform()?"ps1":"sh"}`:null}(t);if(s.installed&&!n.force){const n=f.gray(`(${s.path})`);if(s.latest&&!s.outdated)return nt(f.green(" √ "+v.t("components.up_to_date",{tool:a,current:s.current??"?"}))+n),"upgrade"===e&&nt(f.gray(" "+v.t("components.force_hint",{command:`${rt(t.id)} --force`}))),ot(s,"install"===e?"already_installed":"up_to_date");if("install"===e)return nt(f.green(" √ "+v.t("components.already_installed",{tool:a,current:s.current??"?"}))+n),s.outdated&&nt(f.yellow(" ! "+upgradeAdvice(s))),ot(s,"already_installed",s.outdated?{hint:upgradeAdvice(s)}:{});if(!s.latest)return nt(f.yellow(" ! "+v.t("components.no_latest",{tool:a,current:s.current??"?",how:at(s)}))),ot(s,"manual",{reason:s.error??"no_latest",hint:s.hint});if(!s.auto_upgrade)return nt(f.yellow(" ! "+upgradeAdvice(s))),ot(s,"manual",{hint:s.hint})}if(!i)return nt(f.yellow(" ! "+v.t("components.no_mirror",{tool:a}))),ot(s,"unavailable",{reason:"no_mirror"});s.installed&&!s.auto_upgrade&&nt(f.yellow(" ! "+v.t("components.force_beside",{tool:a,path:s.path??"?",by:s.managed_by??"?"})));const u={};s.installed&&"script"===t.install.type&&Object.assign(u,t.install.forceEnv??{}),"uv"===t.id&&s.installed&&s.auto_upgrade&&s.path&&(u.UV_INSTALL_DIR=c(s.path));const d=t.upgrade?.migrateGlobalPackages&&s.installed&&"fnm"===s.managed_by&&s.path,m=d?globalPackages(s.path,r):[],h=d?V(s.path):null;nt(f.gray(" "+v.t(s.installed?"components.upgrading":"components.installing",{tool:a,latest:s.latest??"",source:s.source}))),k(`component:${t.id}`,"start",{detail:{mode:e,force:Boolean(n.force),current:s.current}});const w=await tt(i,u);j(),J=null;const y=probeRuntime(t,r),b=scriptTarget(t.id,r,{...process.env,...u}),$=(b?y.find(t=>{return e=t.path,n=b,"win32"===r?V(e).toLowerCase()===V(n).toLowerCase():V(e)===V(n);var e,n}):void 0)??y.find(e=>runtimeManager(t.id,e.path,r).auto)??y[0];if(!w||!$)return k(`component:${t.id}`,"fail",{duration_ms:Date.now()-o}),nt(f.red(" × "+v.t("components.script_failed",{tool:a,command:(s.installed?rt:st)(t.id)}))),ot(s,"failed",{reason:w?"not_found_after":"script_failed"});d&&m.length&&h&&V($.path)!==h&&await async function(t,e,n){const o=new Set(globalPackages(e,n).map(t=>t.name)),r=t.filter(t=>!o.has(t.name));if(!r.length)return;nt(f.gray(" "+v.t("components.migrating_globals",{packages:r.map(t=>t.name).join("、")})));const s=function(t,e){const n=c(V(t));return p(n,"win32"===e?"npm.cmd":"npm")}(e,n),a=["install","-g",...r.map(t=>`${t.name}@${t.version}`),`--registry=${E}`,"--no-fund","--no-audit","--loglevel=error"];await et(s,a)||nt(f.yellow(" ! "+v.t("components.migrate_failed",{command:[s,...a].join(" ")})))}(m,$.path,r);const _=y[0];_&&_.path!==$.path&&_.version&&$.version&&x(_.version,$.version)<0&&nt(f.yellow(" ! "+v.t("components.path_shadowed",{tool:a,active:_.path,version:$.version,path:$.path}))),s.latest&&$.version&&x($.version,s.latest)<0&&(X(`${t.id}:${r}:ceiling`,{version:$.version,error:s.latest,checked_at:Date.now()}),nt(f.yellow(" ! "+v.t("components.capped",{tool:a,latest:s.latest,version:$.version})))),k(`component:${t.id}`,"ok",{duration_ms:Date.now()-o,detail:{version:$.version}});const P=s.installed?n.force&&!s.outdated?"reinstalled":"upgraded":"installed";var A;return nt(f.green(" √ "+("installed"===P?v.t("components.installed",{tool:a,version:$.version??"?"}):"reinstalled"===P?v.t("components.reinstalled",{tool:a,version:$.version??"?"}):v.t("components.upgraded",{tool:a,from:s.current??"?",to:$.version??"?"})))+f.gray(`(${$.path})`)),A=c($.path),(process.env.PATH??"").split("win32"===componentPlatform()?";":l).some(t=>"win32"===componentPlatform()?t.toLowerCase()===A.toLowerCase():t===A)||nt(f.gray(" "+v.t("components.new_terminal"))),ot(s,P,{version_after:$.version})}function st(t){return`${h} install ${t}`}function at(t){return t.hint?t.hint:v.t(`components.how_${t.managed_by??"other"}`,{tool:t.name})}export function upgradeAdvice(t){return t.auto_upgrade?v.t("components.outdated_auto",{tool:t.name,current:t.current??"?",latest:t.latest??"?",command:rt(t.id)}):v.t("components.outdated_manual",{tool:t.name,current:t.current??"?",latest:t.latest??"?",by:t.managed_by??"?",how:at(t),command:`${rt(t.id)} --force`})}export function setComponentDepsForTests(t){void 0!==t.platform&&(O=t.platform),void 0!==t.uid&&(U=t.uid),void 0!==t.systemPaths&&(I=t.systemPaths),t.registryPath&&(G=t.registryPath,J=null),t.versionRunner&&(B=t.versionRunner),t.macDeveloperTools&&(F=t.macDeveloperTools),t.fetchText&&(Z=t.fetchText),t.scriptRunner&&(tt=t.scriptRunner),t.commandRunner&&(et=t.commandRunner)}
|
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 l,chmodSync as c}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,isRetiredDefaultModel as y}from"./model-registry.js";import{readLegacySecretsFile as _}from"./secret-store.js";import{brand as k,brandEnv as b}from"./brand.js";export const DEFAULT_BASE_URL=k().gatewayAddresses[0].url;export const DEFAULT_MODELS=d();export const DEFAULT_PROFILE_NAME="default";const v="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.record(`Migrated config dir: ${t} -> ${this.configDir}`)}catch(e){u.warn("Failed to migrate legacy .chelper config dir:",e)}}resolveConfigDir(){const e=b("CONFIG_DIR");if(e&&e.trim())return e.trim();const t=b("TEST_HOME");return t&&t.trim()?f(t.trim(),k().configDirName):f(h(),k().configDirName)}createDefaultConfig(){return this.syncLegacyFields({lang:"en_US",active_profile:"default",profiles:{[DEFAULT_PROFILE_NAME]:{}},presets:{}})}normalizeModels(e){const t=t=>{const i=e?.[t]?.trim();return i&&!y(t,i)?i:DEFAULT_MODELS[t]},i={haiku:t("haiku"),sonnet:t("sonnet"),opus:t("opus"),reasoning:t("reasoning")};if(e&&"object"==typeof e)for(const[t,o]of Object.entries(e))t in i||"string"==typeof o&&o.trim()&&(i[t]=o.trim());return i}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!==v)try{s=this.mergeLegacySecrets(s,_(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{c(this.configPath,384)}catch{}return s}const t=this.getLegacySecretsPath();if(e(t))try{const e=this.mergeLegacySecrets(this.createDefaultConfig(),_(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",...!0===o.lang_chosen?{lang_chosen:!0}:{},...o.credential_store===v?{credential_store:v}:{},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{c(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:l(f(t,e)).mtime})).sort((e,t)=>t.mtime.getTime()-e.mtime.getTime()),c=new Set;a.slice(i).forEach(e=>c.add(e.path)),a.forEach(e=>{e.mtime.getTime()<s&&c.add(e.path)});for(const t of c){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:v};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"}isLangChosen(){return!0===this.config.lang_chosen}setLang(e){this.updateConfig({lang:e,lang_chosen:!0})}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:l(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),l=f(r,a?`config-${n}-${a}.yaml`:`config-${n}.yaml`),c=e(this.configPath)?i(this.configPath,"utf-8"):m.dump(this.prepareForPersistence(this.getConfig()));return o(l,c,{encoding:"utf-8",mode:384}),this.rotateBackups(r,10,30),l}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 l=this.migrateOldConfig(n||this.createDefaultConfig());if(n?.credential_store!==v&&e(a))try{l=this.mergeLegacySecrets(l,_(a))}catch(e){throw new Error(`Legacy backup credentials could not be decrypted: ${a}`,{cause:e})}this.saveConfig(l)}getConfigDir(){return this.configDir}getBaseUrl(){return this.runtimeBaseUrl||this.config.profiles?.[this.getActiveProfileName()]?.base_url||DEFAULT_BASE_URL}hasRuntimeBaseUrl(){return Boolean(this.runtimeBaseUrl)}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 d,isRetiredDefaultModel as y}from"./model-registry.js";import{readLegacySecretsFile as _}from"./secret-store.js";import{brand as k,brandEnv as b}from"./brand.js";export const DEFAULT_BASE_URL=k().gatewayAddresses[0].url;export const DEFAULT_MODELS=d();export const DEFAULT_PROFILE_NAME="default";const v="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.record(`Migrated config dir: ${t} -> ${this.configDir}`)}catch(e){u.warn("Failed to migrate legacy .chelper config dir:",e)}}resolveConfigDir(){const e=b("CONFIG_DIR");if(e&&e.trim())return e.trim();const t=b("TEST_HOME");return t&&t.trim()?f(t.trim(),k().configDirName):f(h(),k().configDirName)}createDefaultConfig(){return this.syncLegacyFields({lang:"en_US",active_profile:"default",profiles:{[DEFAULT_PROFILE_NAME]:{}},presets:{}})}normalizeModels(e){const t=t=>{const i=e?.[t]?.trim();return i&&!y(t,i)?i:DEFAULT_MODELS[t]},i={haiku:t("haiku"),sonnet:t("sonnet"),opus:t("opus"),reasoning:t("reasoning")};if(e&&"object"==typeof e)for(const[t,o]of Object.entries(e))t in i||"string"==typeof o&&o.trim()&&(i[t]=o.trim());return i}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!==v)try{s=this.mergeLegacySecrets(s,_(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(),_(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",...!0===o.lang_chosen?{lang_chosen:!0}:{},...o.credential_store===v?{credential_store:v}:{},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:v};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"}isLangChosen(){return!0===this.config.lang_chosen}setLang(e){this.updateConfig({lang:e,lang_chosen:!0})}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!==v&&e(a))try{c=this.mergeLegacySecrets(c,_(a))}catch(e){throw new Error(`Legacy backup credentials could not be decrypted: ${a}`,{cause:e})}this.saveConfig(c)}getConfigDir(){return this.configDir}getBaseUrl(){return this.runtimeBaseUrl||this.config.profiles?.[this.getActiveProfileName()]?.base_url||DEFAULT_BASE_URL}hasRuntimeBaseUrl(){return Boolean(this.runtimeBaseUrl)}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();export function savedApiKeys(){const e=new Set;for(const t of configManager.listProfiles()){const i=configManager.getProfile(t);for(const t of[i.api_key,...Object.values(i.tool_api_keys??{})])t?.trim()&&e.add(t.trim())}return[...e]}
|
package/dist/lib/dry-run.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{firstCommandWord as e}from"./cli-help.js";export function dryRunUnsupportedCommand(s){const n=e(s);if(void 0===n)return"(wizard)";const r=s.filter(e=>!e.startsWith("-")),o=r[r.indexOf(n)+1];switch(n){case"setup":case"config":case"status":case"bench":case"logs":case"completion":case"help":return null;case"doctor":return s.includes("--report")?"doctor --report":null;case"base":return void 0===o?null:`base ${o}`;case"lang":return void 0===o||"show"===o?null:`lang ${o}`;case"codex":return s.includes("--undo")?"codex sessions --undo":null;case"sessions":return"export"===o?"sessions export":"resume"===o&&s.includes("--run")?"sessions resume --run":null;default:return n}}
|
|
1
|
+
import{firstCommandWord as e}from"./cli-help.js";export function dryRunUnsupportedCommand(s){const n=e(s);if(void 0===n)return"(wizard)";const r=s.filter(e=>!e.startsWith("-")),o=r[r.indexOf(n)+1];switch(n){case"setup":case"config":case"status":case"bench":case"logs":case"list":case"completion":case"help":return null;case"doctor":return s.includes("--report")?"doctor --report":null;case"base":return void 0===o?null:`base ${o}`;case"lang":return void 0===o||"show"===o?null:`lang ${o}`;case"codex":return s.includes("--undo")?"codex sessions --undo":null;case"sessions":return"export"===o?"sessions export":"resume"===o&&s.includes("--run")?"sessions resume --run":null;default:return n}}
|
package/dist/lib/env-cleanup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as e,readFileSync as
|
|
1
|
+
import{existsSync as e,mkdirSync as o,readFileSync as r,writeFileSync as n}from"fs";import{join as t}from"path";import{logger as s}from"../utils/logger.js";import{toolHome as c}from"./tools/types.js";import{backupBeforeWrite as i}from"./tools/backup.js";import{configManager as u,savedApiKeys as f}from"./config.js";import{isOurGatewayUrl as l}from"./gateway-addresses.js";import{broadcastEnvChange as a,deleteWindowsEnv as p,readWindowsEnv as m}from"./windows-env.js";export const ANTHROPIC_ENV_VARS=["ANTHROPIC_API_KEY","ANTHROPIC_AUTH_TOKEN","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=c();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")}}function h(e){const o=e.trim().replace(/\s+#.*$/,""),r=o.match(/^(['"])(.*)\1$/);return r?r[2]:o}const v=e=>new RegExp(String.raw`^\s*export\s+${e}=(.*)$`,"gm"),E=e=>new RegExp(String.raw`^[ \t]*\$env:${e}[ \t]*=(.*)$`,"gim");function g(e){if(l(e.ANTHROPIC_BASE_URL))return!0;const o=f();return[e.ANTHROPIC_API_KEY,e.ANTHROPIC_AUTH_TOKEN].some(e=>Boolean(e)&&o.includes(e.trim()))}export function anthropicEnvSources(){const o=[],n=(n,t)=>{if(n&&e(n))try{const e=function(e,o){const r={};for(const n of ANTHROPIC_ENV_VARS)for(const t of e.matchAll(o(n)))r[n]=h(t[1]);return r}(r(n,"utf-8"),t);Object.keys(e).length>0&&o.push({kind:"file",location:n,vars:e,ours:g(e)})}catch(e){s.logError("envCleanup.read",e)}};if("win32"!==process.platform)return n(getShellRcFilePath(),v),o;for(const e of["user","machine"]){const r={};for(const o of ANTHROPIC_ENV_VARS){const n=m(e,o);void 0!==n&&(r[o]=n)}Object.keys(r).length>0&&o.push({kind:"user"===e?"windows-user":"windows-machine",location:"user"===e?"HKCU\\Environment":"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",vars:r,ours:g(r)})}for(const e of function(){const e=c(),o=[];for(const r of[t(e,"Documents"),t(e,"OneDrive","Documents"),t(e,"OneDrive","文档")])for(const e of["PowerShell","WindowsPowerShell"])o.push(t(r,e,"Microsoft.PowerShell_profile.ps1"),t(r,e,"profile.ps1"));return o}())n(e,E);return o}function O(e,o,t){const c=r(e,"utf-8");let u=c;for(const e of o)u=u.replace(new RegExp(t(e).source+String.raw`(\r?\n)?`,t(e).flags),"");return t===v&&(u=u.replace(/^\s*#\s*Claude Code environment variables\s*$\n?/gm,"")),u!==c&&(i(e),n(e,u,"utf-8"),s.debug(`Removed ${o.join(", ")} from ${e}`),!0)}function _(e,r){try{const s=t(u.getConfigDir(),"backups");o(s,{recursive:!0});const c=t(s,`windows-env-${e}-${(new Date).toISOString().replace(/[:.]/g,"-")}.json`);return n(c,JSON.stringify(r,null,2)+"\n",{mode:384}),c}catch(e){return s.logError("envCleanup.backupWindowsValues",e),null}}let A={removed:[],foreign:[],machineLeft:[]};export function cleanupAnthropicEnvVars(){const e=anthropicEnvSources(),o=removeEnvSources(e.filter(e=>e.ours));return o.foreign=e.filter(e=>!e.ours),A=o,o}export function removeEnvSources(e){const o={removed:[],foreign:[],machineLeft:[]};let r=!1;for(const n of e){const e=Object.keys(n.vars);try{if("file"===n.kind){O(n.location,e,n.location.endsWith(".ps1")?E:v)&&o.removed.push(n);continue}const t="windows-user"===n.kind?"user":"machine";_(t,n.vars);const c=[];for(const r of e)try{p(t,r),c.push(r)}catch(e){"machine"===t&&o.machineLeft.push(r),s.logError("envCleanup.deleteWindowsEnv",e)}c.length>0&&(r=!0,o.removed.push({...n,vars:Object.fromEntries(c.map(e=>[e,n.vars[e]]))}))}catch(e){s.logError("envCleanup.removeEnvSources",e)}}r&&a();for(const e of o.removed)for(const[o,r]of Object.entries(e.vars))process.env[o]===r&&delete process.env[o];return o}export function lastEnvCleanup(){return A}export function envCleanupPlan(){return anthropicEnvSources().filter(e=>e.ours)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as r,mkdirSync as e,readFileSync as t,writeFileSync as o}from"fs";import{join as s}from"path";import{configManager as n}from"./config.js";import{
|
|
1
|
+
import{existsSync as r,mkdirSync as e,readFileSync as t,writeFileSync as o}from"fs";import{join as s}from"path";import{configManager as n}from"./config.js";import{noteGatewayUrl as a,wroteGatewayUrl as i}from"./written-values.js";import{getBuiltinGatewayAddresses as l}from"./model-registry.js";import{logger as u}from"../utils/logger.js";const c=new Set(["SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","UNABLE_TO_VERIFY_LEAF_SIGNATURE","UNABLE_TO_GET_ISSUER_CERT_LOCALLY","CERT_HAS_EXPIRED","CERT_NOT_YET_VALID","ERR_TLS_CERT_ALTNAME_INVALID"]);function f(){return s(n.getConfigDir(),"gateway-addresses.json")}export function normalizeBaseUrl(r){let e,t=r.trim();if(!t)return null;/^[a-z][a-z0-9+.-]*:\/\//i.test(t)||(t=`https://${t}`);try{e=new URL(t)}catch{return null}if("https:"!==e.protocol&&"http:"!==e.protocol)return null;const o=e.pathname.replace(/\/+$/,"").replace(/\/v1$/i,"");return`${e.protocol}//${e.host}${o}`}export function parsePublishedAddresses(r){if(!Array.isArray(r))return[];const e=new Set,t=[];for(const o of r){if(!o||"object"!=typeof o)continue;const{url:r,route:s,description:n}=o,a="string"==typeof r?normalizeBaseUrl(r):null;if(!a?.startsWith("https://")||e.has(a))continue;e.add(a);const i="string"==typeof s?s.trim():"",l="string"==typeof n?n.trim():"";t.push({url:a,label:i&&l?`${i}(${l})`:i||l})}return t}export function rememberPublishedAddresses(r,t){if(!r||"object"!=typeof r)return;if(!l().some(r=>normalizeBaseUrl(r.url)===t))return;const s=r;if(!1===s.api_info_enabled)return;const a=parsePublishedAddresses(s.api_info);if(0!==a.length)try{e(n.getConfigDir(),{recursive:!0}),o(f(),JSON.stringify({addresses:a},null,2)+"\n")}catch(r){u.logError("rememberPublishedAddresses",r)}}export function gatewayAddresses(){const e=function(){const e=f();if(!r(e))return[];try{const r=JSON.parse(t(e,"utf-8"));return(Array.isArray(r.addresses)?r.addresses:[]).flatMap(r=>{const e="string"==typeof r?.url?normalizeBaseUrl(r.url):null;return e?[{url:e,label:"string"==typeof r.label?r.label:""}]:[]})}catch{return[]}}(),o=[];for(const r of l()){const t=normalizeBaseUrl(r.url);if(!t||o.some(r=>r.url===t))continue;const s=e.find(r=>r.url===t)?.label||r.label;o.push({url:t,label:s})}for(const r of e)o.some(e=>e.url===r.url)||o.push(r);return o}export function isOurGatewayUrl(r){if("string"!=typeof r)return!1;const e=normalizeBaseUrl(r);return!!e&&(gatewayAddresses().some(r=>r.url===e)||e===normalizeBaseUrl(n.getBaseUrl())||i(e))}export function noteWrittenGatewayUrl(r){const e=normalizeBaseUrl(r);e&&a(e)}function m(r){const e=r?.cause,t="string"==typeof e?.code?e.code:"",o="string"==typeof e?.message?e.message:r instanceof Error?r.message:String(r);return r instanceof Error&&"AbortError"===r.name?{error:"timeout",detail:o}:c.has(t)||/certificate/i.test(o)?{error:"certificate",detail:t||o}:{error:"network",detail:t||o}}export async function probeGatewayAddress(r,e={}){e.apiKey&&!function(r){return r===n.getBaseUrl()||l().some(e=>normalizeBaseUrl(e.url)===r)}(r)&&(e={...e,apiKey:void 0});const t=Math.max(1,e.samples??1),o=[];let s;for(let n=0;n<t;n+=1){const t=await d(r,e);if(!t.ok)return t;o.push(t.ms),s=t.models}return o.sort((r,e)=>r-e),{url:r,ok:!0,ms:o[Math.floor(o.length/2)],...void 0===s?{}:{models:s}}}async function d(r,e){const{apiKey:t,timeoutMs:o=8e3,fetchFn:s=fetch}=e,n=new AbortController,a=setTimeout(()=>n.abort(),o),i=Date.now();try{const e=await s(t?`${r}/v1/models`:`${r}/api/status`,{method:"GET",headers:t?{Authorization:`Bearer ${t}`}:void 0,signal:n.signal});let o;try{o=(await e.json())?.data}catch{o=void 0}const a=Date.now()-i;return!t||401!==e.status&&403!==e.status?t?e.ok&&Array.isArray(o)?{url:r,ok:!0,ms:a,models:o.length}:{url:r,ok:!1,ms:a,error:"not_gateway",detail:`HTTP ${e.status}`}:e.ok&&o&&"object"==typeof o?(rememberPublishedAddresses(o,r),{url:r,ok:!0,ms:a}):{url:r,ok:!1,ms:a,error:"not_gateway",detail:`HTTP ${e.status}`}:{url:r,ok:!1,ms:a,error:"key_rejected",detail:`HTTP ${e.status}`}}catch(e){return{url:r,ok:!1,ms:Date.now()-i,...m(e)}}finally{clearTimeout(a)}}
|
package/dist/lib/i18n.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as t,existsSync as e,readdirSync as s}from"fs";import{join as o,dirname as r}from"path";import{fileURLToPath as a}from"url";import{logger as n}from"../utils/logger.js";import{brandTokens as l}from"./brand.js";class c{static instance;currentLocale="en_US";translations=new Map;fallbackLocale="en_US";localesDir;constructor(){const t=a(import.meta.url),e=r(t);this.localesDir=o(e,"..","locales"),this.loadTranslations(),this.loadSavedLocale()}static getInstance(){return c.instance||(c.instance=new c),c.instance}loadTranslations(){try{if(!e(this.localesDir))return void n.warn(`Locales directory not found at ${this.localesDir}`);const r=s(this.localesDir).filter(t=>t.endsWith(".json"));if(0===r.length)return void n.warn("No locale files found in locales directory");for(const e of r){const s=e.replace(".json",""),r=o(this.localesDir,e);try{const e=t(r,"utf-8"),o=JSON.parse(e);this.translations.set(s,o)}catch(t){n.warn(`Failed to load locale file ${e}:`,t)}}const a=process.env.LANG||process.env.LC_ALL||process.env.LC_MESSAGES;if(a){const t=a.split(".")[0].replace("_","-");if(this.translations.has(t))this.currentLocale=t;else{const e=t.split("-")[0],s=Array.from(this.translations.keys()).find(t=>t.startsWith(e+"_"));s?this.currentLocale=s:"zh"===e?this.currentLocale="zh_CN":"en"===e&&(this.currentLocale="en_US")}}}catch(t){n.warn("Failed to load translations:",t)}}setLocale(t){this.translations.has(t)?(this.currentLocale=t,this.saveLocale()):n.warn(`Locale '${t}' not supported, falling back to '${this.currentLocale}'`)}getLocale(){return this.currentLocale}getAvailableLocales(){return Array.from(this.translations.keys())}translate(t,e){const s=t.split(".");let o=this.translations.get(this.currentLocale);for(const e of s){if(!o||"object"!=typeof o||!(e in o)){o=this.translations.get(this.fallbackLocale);for(const e of s){if(!o||"object"!=typeof o||!(e in o))return t;o=o[e]}break}o=o[e]}if("string"!=typeof o)return t;const r=l(this.currentLocale);return o=o.replace(/\{\{(\w+)\}\}/g,(t,e)=>r[e]??t),e?o.replace(/\{\{(\w+)\}\}/g,(t,s)=>e[s]
|
|
1
|
+
import{readFileSync as t,existsSync as e,readdirSync as s}from"fs";import{join as o,dirname as r}from"path";import{fileURLToPath as a}from"url";import{logger as n}from"../utils/logger.js";import{brandTokens as l}from"./brand.js";class c{static instance;currentLocale="en_US";translations=new Map;fallbackLocale="en_US";localesDir;constructor(){const t=a(import.meta.url),e=r(t);this.localesDir=o(e,"..","locales"),this.loadTranslations(),this.loadSavedLocale()}static getInstance(){return c.instance||(c.instance=new c),c.instance}loadTranslations(){try{if(!e(this.localesDir))return void n.warn(`Locales directory not found at ${this.localesDir}`);const r=s(this.localesDir).filter(t=>t.endsWith(".json"));if(0===r.length)return void n.warn("No locale files found in locales directory");for(const e of r){const s=e.replace(".json",""),r=o(this.localesDir,e);try{const e=t(r,"utf-8"),o=JSON.parse(e);this.translations.set(s,o)}catch(t){n.warn(`Failed to load locale file ${e}:`,t)}}const a=process.env.LANG||process.env.LC_ALL||process.env.LC_MESSAGES;if(a){const t=a.split(".")[0].replace("_","-");if(this.translations.has(t))this.currentLocale=t;else{const e=t.split("-")[0],s=Array.from(this.translations.keys()).find(t=>t.startsWith(e+"_"));s?this.currentLocale=s:"zh"===e?this.currentLocale="zh_CN":"en"===e&&(this.currentLocale="en_US")}}}catch(t){n.warn("Failed to load translations:",t)}}setLocale(t){this.translations.has(t)?(this.currentLocale=t,this.saveLocale()):n.warn(`Locale '${t}' not supported, falling back to '${this.currentLocale}'`)}getLocale(){return this.currentLocale}getAvailableLocales(){return Array.from(this.translations.keys())}translate(t,e){const s=t.split(".");let o=this.translations.get(this.currentLocale);for(const e of s){if(!o||"object"!=typeof o||!(e in o)){o=this.translations.get(this.fallbackLocale);for(const e of s){if(!o||"object"!=typeof o||!(e in o))return t;o=o[e]}break}o=o[e]}if("string"!=typeof o)return t;const r=l(this.currentLocale);return o=o.replace(/\{\{(\w+)\}\}/g,(t,e)=>r[e]??t),e?o.replace(/\{\{(\w+)\}\}/g,(t,s)=>e[s]??t):o}t(t,e){return this.translate(t,e)}saveLocale(){}loadSavedLocale(){}loadFromConfig(t){this.translations.has(t)&&(this.currentLocale=t)}}export function detectSystemLang(){const t=process.env.LC_ALL||process.env.LC_MESSAGES||process.env.LANG||process.env.LANGUAGE||"";let e="";try{e=Intl.DateTimeFormat().resolvedOptions().locale||""}catch{}return`${t} ${e}`.toLowerCase().split(/[\s:;,]+/).some(t=>"zh"===t||t.startsWith("zh-")||t.startsWith("zh_"))?"zh_CN":"en_US"}export{c as I18n};export const i18n=c.getInstance();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dirname as e,join as
|
|
1
|
+
import{dirname as e,join as t}from"path";import{fileURLToPath as o}from"url";import{existsSync as l,readFileSync as n}from"fs";import{currentClientConfig as r,usableClientSection as s}from"./client-config.js";import{brand as a}from"./brand.js";const i=e(o(import.meta.url)),d=(e=[],t=[])=>[...new Set([...e,...t])];export function overlayClientSection(e,t){if(!t)return e;const o={...e.retiredDefaultModels??{}};for(const[e,l]of Object.entries(t.retiredDefaultModels??{}))o[e]=d(o[e],l);return{...e,defaultModels:{...e.defaultModels,...t.defaultModels??{}},retiredDefaultModels:o,toolDefaults:{...e.toolDefaults??{},...t.toolDefaults??{}},claudeCode:t.tierChains?{...e.claudeCode??{},tierChains:{...e.claudeCode?.tierChains??{},...t.tierChains}}:e.claudeCode,claudeDesktop:t.oneMillionFallback?{...e.claudeDesktop??{},oneMillionFallback:[...t.oneMillionFallback]}:e.claudeDesktop,gatewayModels:t.retiredModels?{...e.gatewayModels??{},retired:d(e.gatewayModels?.retired,t.retiredModels)}:e.gatewayModels,toolMinVersions:{...e.toolMinVersions??{},...t.toolMinVersions??{}},agy:t.agyModels?{...e.agy??{},gatewayModels:[...t.agyModels]}:e.agy}}let u=null;function c(){return u||(u=overlayClientSection(function(){const e=[t(i,"..","..","models.json"),t(i,"..","models.json"),t(i,"models.json")];for(const t of e)if(l(t))return JSON.parse(n(t,"utf-8"));throw new Error(`models.json not found, searched: ${e.join(", ")}`)}(),s(r()?.config))),u}export function getDefaultModels(){return{...c().defaultModels}}export function isRetiredDefaultModel(e,t){return c().retiredDefaultModels?.[e]?.includes(t)??!1}export function getRetiredDefaultModelIds(){return[...new Set(Object.values(c().retiredDefaultModels??{}).flat())]}export function getDefaultToolModel(){return c().defaultToolModel?.trim()||"gpt-5.5"}export function getToolDefaultModel(e){const t=c().toolDefaults;return t&&Object.prototype.hasOwnProperty.call(t,e)?(t[e]??"").trim():getDefaultToolModel()}export function getPresets(){return[...c().presets]}export function resolvePreset(e){return e.models?{...e.models}:e.model?{haiku:e.model,sonnet:e.model,opus:e.model,reasoning:e.model}:getDefaultModels()}export function getClaudeCodeTierChains(){const e=c().claudeCode?.tierChains??{};return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,[...t]]))}export function getClaudeDesktopRules(){const e=c().claudeDesktop??{};return{oneMillionFallback:[...e.oneMillionFallback??[]],oneMillionExclusions:(e.oneMillionExclusions??[]).map(e=>({...e,models:[...e.models]}))}}export function getGatewayModelRules(){const e=c().gatewayModels??{};return{autoCompactRatio:e.autoCompactRatio??.8,models:(e.models??[]).map(e=>({...e})),retired:[...e.retired??[]]}}export function getBuiltinGatewayAddresses(){return a().gatewayAddresses.map(e=>({...e}))}export function getCodexRules(){return{standardContextWindow:c().codex?.standardContextWindow??272e3}}export function getAgyRules(){return{gatewayModels:[...c().agy?.gatewayModels??[]]}}export function compareVersions(e,t){const o=e.split(".").map(e=>parseInt(e,10)||0),l=t.split(".").map(e=>parseInt(e,10)||0);for(let e=0;e<Math.max(o.length,l.length);e++){const t=(o[e]??0)-(l[e]??0);if(0!==t)return t}return 0}export function getToolMinVersion(e){return c().toolMinVersions?.[e]?.trim()||null}export function reloadRegistry(){u=null}
|