@simonyea/holysheep-cli 2.1.70 → 2.1.72

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.
@@ -1008,7 +1008,7 @@ var require_which = __commonJS({
1008
1008
  "src/utils/which.js"(exports2, module2) {
1009
1009
  var path = require("path");
1010
1010
  var fs = require("fs");
1011
- var { exec, execSync } = require("child_process");
1011
+ var { exec, execSync, execFileSync } = require("child_process");
1012
1012
  function canRun(command, options = {}) {
1013
1013
  try {
1014
1014
  execSync(command, { stdio: "ignore", ...options });
@@ -1026,8 +1026,22 @@ var require_which = __commonJS({
1026
1026
  });
1027
1027
  }
1028
1028
  __name(canRunAsync, "canRunAsync");
1029
+ function isWindowsLike() {
1030
+ return process.platform === "win32" || process.env.HOLYSHEEP_TEST_PLATFORM === "win32";
1031
+ }
1032
+ __name(isWindowsLike, "isWindowsLike");
1033
+ function appendDirToProcessPath(dir) {
1034
+ if (!dir) return false;
1035
+ const delimiter = isWindowsLike() ? ";" : path.delimiter;
1036
+ const parts = (process.env.PATH || "").split(delimiter).map((item) => item.trim()).filter(Boolean);
1037
+ const hasDir = parts.some((item) => item.toLowerCase() === dir.toLowerCase());
1038
+ if (hasDir) return false;
1039
+ process.env.PATH = [...parts, dir].join(delimiter);
1040
+ return true;
1041
+ }
1042
+ __name(appendDirToProcessPath, "appendDirToProcessPath");
1029
1043
  function getWindowsKnownPaths(cmd) {
1030
- if (process.platform !== "win32") return [];
1044
+ if (!isWindowsLike()) return [];
1031
1045
  const home = process.env.USERPROFILE || process.env.HOME || "";
1032
1046
  const appData = process.env.APPDATA || "";
1033
1047
  const localAppData = process.env.LOCALAPPDATA || "";
@@ -1048,7 +1062,10 @@ var require_which = __commonJS({
1048
1062
  function checkKnownPathsSync(cmd) {
1049
1063
  for (const p of getWindowsKnownPaths(cmd)) {
1050
1064
  try {
1051
- if (fs.existsSync(p)) return p;
1065
+ if (fs.existsSync(p)) {
1066
+ appendDirToProcessPath(path.dirname(p));
1067
+ return p;
1068
+ }
1052
1069
  } catch {
1053
1070
  }
1054
1071
  }
@@ -1057,14 +1074,15 @@ var require_which = __commonJS({
1057
1074
  __name(checkKnownPathsSync, "checkKnownPathsSync");
1058
1075
  var _userPathRefreshedAt = 0;
1059
1076
  function refreshWindowsUserPath() {
1060
- if (process.platform !== "win32") return;
1077
+ if (!isWindowsLike()) return;
1061
1078
  const now = Date.now();
1062
1079
  if (now - _userPathRefreshedAt < 5e3) return;
1063
1080
  _userPathRefreshedAt = now;
1064
1081
  try {
1065
- const out = execSync(
1066
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::GetEnvironmentVariable('Path', 'User')"`,
1067
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 }
1082
+ const out = execFileSync(
1083
+ "powershell.exe",
1084
+ ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", "[Environment]::GetEnvironmentVariable('Path', 'User')"],
1085
+ { encoding: "utf8", windowsHide: true, timeout: 5e3 }
1068
1086
  ).trim();
1069
1087
  if (!out) return;
1070
1088
  const userParts = out.split(";").map((s) => s.trim()).filter(Boolean);
@@ -1085,7 +1103,7 @@ var require_which = __commonJS({
1085
1103
  }
1086
1104
  __name(refreshWindowsUserPath, "refreshWindowsUserPath");
1087
1105
  function commandExists(cmd) {
1088
- if (process.platform === "win32") {
1106
+ if (isWindowsLike()) {
1089
1107
  const variants = [cmd, `${cmd}.cmd`, `${cmd}.exe`, `${cmd}.bat`];
1090
1108
  for (const variant of variants) {
1091
1109
  if (canRun(`where ${variant}`)) return true;
@@ -1105,7 +1123,7 @@ var require_which = __commonJS({
1105
1123
  }
1106
1124
  __name(commandExists, "commandExists");
1107
1125
  async function commandExistsAsync(cmd) {
1108
- if (process.platform === "win32") {
1126
+ if (isWindowsLike()) {
1109
1127
  const variants = [cmd, `${cmd}.cmd`, `${cmd}.exe`, `${cmd}.bat`];
1110
1128
  for (const variant of variants) {
1111
1129
  if (await canRunAsync(`where ${variant}`)) return true;
@@ -1129,7 +1147,9 @@ var require_which = __commonJS({
1129
1147
  commandExistsAsync,
1130
1148
  // test-only exports
1131
1149
  _getWindowsKnownPaths: getWindowsKnownPaths,
1132
- _refreshWindowsUserPath: refreshWindowsUserPath
1150
+ _refreshWindowsUserPath: refreshWindowsUserPath,
1151
+ _checkKnownPathsSync: checkKnownPathsSync,
1152
+ _appendDirToProcessPath: appendDirToProcessPath
1133
1153
  };
1134
1154
  }
1135
1155
  });
@@ -4135,11 +4155,11 @@ var require_package = __commonJS({
4135
4155
  "package.json"(exports2, module2) {
4136
4156
  module2.exports = {
4137
4157
  name: "@simonyea/holysheep-cli",
4138
- version: "2.1.70",
4158
+ version: "2.1.72",
4139
4159
  description: "Claude Code/Cursor/Cline API relay for China \u2014 \xA51=$1, WeChat/Alipay payment, no credit card, no VPN. One command setup for all AI coding tools.",
4140
4160
  scripts: {
4141
4161
  build: "node scripts/build.mjs",
4142
- test: "node tests/droid.test.js && node tests/workspace-store.test.js && node tests/runtime-stale-upgrade.test.js && node tests/hermes.test.js && node tests/preflight.test.js && node tests/opencode-auth-purge.test.js && node tests/shell-winpath.test.js && node tests/openclaw-atomic-write.test.js && node tests/openclaw-disable-auth-direct.test.js && node tests/opencode-default-model.test.js && node tests/paths-bundled.test.js && node tests/aionui-runtime-resources.test.js && node tests/aionui-wrapper-claude-proxy.test.js && node tests/aionui-wrapper-probe.test.js && node tests/aionui-wrapper-proxy-integration.test.js && node tests/aionui-wrapper-all-clis-autoconf.test.js && node tests/aionui-wrapper-env-signal.test.js && node tests/aionui-wrapper-csp-rewrite.test.js && node tests/aionui-wrapper-version-status.test.js && node tests/claude-proxy-daemon.test.js && node tests/claude-proxy-vscode-settings.test.js && node tests/claude-proxy-vscode-env.test.js && node tests/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js && node tests/claude-code-configure-preserve.test.js && node tests/webui-claude-proxy-virtual-tool.test.js && node tests/webui-claude-proxy-ui.test.js && node tests/webui-response-consumer.test.js",
4162
+ test: "node tests/droid.test.js && node tests/workspace-store.test.js && node tests/runtime-stale-upgrade.test.js && node tests/hermes.test.js && node tests/preflight.test.js && node tests/opencode-auth-purge.test.js && node tests/shell-winpath.test.js && node tests/openclaw-atomic-write.test.js && node tests/openclaw-disable-auth-direct.test.js && node tests/opencode-default-model.test.js && node tests/paths-bundled.test.js && node tests/aionui-runtime-resources.test.js && node tests/aionui-wrapper-claude-proxy.test.js && node tests/aionui-wrapper-probe.test.js && node tests/aionui-wrapper-proxy-integration.test.js && node tests/aionui-wrapper-all-clis-autoconf.test.js && node tests/aionui-wrapper-env-signal.test.js && node tests/aionui-wrapper-csp-rewrite.test.js && node tests/aionui-wrapper-version-status.test.js && node tests/claude-proxy-daemon.test.js && node tests/claude-proxy-vscode-settings.test.js && node tests/claude-proxy-vscode-env.test.js && node tests/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js && node tests/claude-code-configure-preserve.test.js && node tests/webui-claude-proxy-virtual-tool.test.js && node tests/webui-claude-proxy-ui.test.js && node tests/webui-response-consumer.test.js && node tests/webui-claude-proxy-install-gating.test.js && node tests/claude-code-windows-local-bin.test.js",
4143
4163
  prepublishOnly: "npm run build && npm test && node scripts/check-tarball-size.js"
4144
4164
  },
4145
4165
  keywords: [
@@ -4224,13 +4244,59 @@ var require_shell = __commonJS({
4224
4244
  var fs = require("fs");
4225
4245
  var path = require("path");
4226
4246
  var os = require("os");
4227
- var { execSync } = require("child_process");
4247
+ var { execSync, execFileSync } = require("child_process");
4228
4248
  var pkg = require_package();
4229
4249
  var MARKER_START = "# >>> holysheep-cli managed >>>";
4230
4250
  var MARKER_END = "# <<< holysheep-cli managed <<<";
4251
+ function isWindowsLike() {
4252
+ return process.platform === "win32" || process.env.HOLYSHEEP_TEST_PLATFORM === "win32";
4253
+ }
4254
+ __name(isWindowsLike, "isWindowsLike");
4255
+ var execFileSyncOverride = null;
4256
+ function quotePowerShellString(value) {
4257
+ return `'${String(value).replace(/'/g, "''")}'`;
4258
+ }
4259
+ __name(quotePowerShellString, "quotePowerShellString");
4260
+ function runPowerShellUserEnvScript(script, options = {}) {
4261
+ const runner = execFileSyncOverride || execFileSync;
4262
+ try {
4263
+ return runner("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], {
4264
+ encoding: "utf8",
4265
+ windowsHide: true,
4266
+ timeout: options.timeout || 5e3
4267
+ });
4268
+ } catch (err) {
4269
+ const stderr = err && err.stderr ? String(err.stderr).trim() : "";
4270
+ const stdout = err && err.stdout ? String(err.stdout).trim() : "";
4271
+ const detail = stderr || stdout || err && err.message || "unknown error";
4272
+ throw new Error(`PowerShell \u7528\u6237\u73AF\u5883\u53D8\u91CF\u64CD\u4F5C\u5931\u8D25: ${detail}`);
4273
+ }
4274
+ }
4275
+ __name(runPowerShellUserEnvScript, "runPowerShellUserEnvScript");
4276
+ function getWindowsUserEnvVar(name) {
4277
+ return runPowerShellUserEnvScript(`[Environment]::GetEnvironmentVariable(${quotePowerShellString(name)}, 'User')`).trim();
4278
+ }
4279
+ __name(getWindowsUserEnvVar, "getWindowsUserEnvVar");
4280
+ function setWindowsUserEnvVar(name, value) {
4281
+ runPowerShellUserEnvScript(`[Environment]::SetEnvironmentVariable(${quotePowerShellString(name)}, ${value === null || value === void 0 ? "$null" : quotePowerShellString(value)}, 'User')`);
4282
+ }
4283
+ __name(setWindowsUserEnvVar, "setWindowsUserEnvVar");
4284
+ function splitWindowsPath(value) {
4285
+ return String(value || "").split(";").map((item) => item.trim()).filter(Boolean);
4286
+ }
4287
+ __name(splitWindowsPath, "splitWindowsPath");
4288
+ function appendDirToProcessPath(dir) {
4289
+ const parts = splitWindowsPath(process.env.PATH);
4290
+ if (!parts.some((item) => item.toLowerCase() === dir.toLowerCase())) {
4291
+ process.env.PATH = [...parts, dir].join(";");
4292
+ return true;
4293
+ }
4294
+ return false;
4295
+ }
4296
+ __name(appendDirToProcessPath, "appendDirToProcessPath");
4231
4297
  function getShellRcFiles() {
4232
4298
  const home = os.homedir();
4233
- if (process.platform === "win32") return [];
4299
+ if (isWindowsLike()) return [];
4234
4300
  const shell = process.env.SHELL || "";
4235
4301
  const candidates = [];
4236
4302
  if (shell.includes("zsh")) candidates.push(path.join(home, ".zshrc"));
@@ -4276,16 +4342,13 @@ var require_shell = __commonJS({
4276
4342
  }
4277
4343
  __name(buildEnvBlock, "buildEnvBlock");
4278
4344
  function ensureWindowsUserPathHasNpmBin() {
4279
- if (process.platform !== "win32") return [];
4345
+ if (!isWindowsLike()) return [];
4280
4346
  const appData = process.env.APPDATA;
4281
4347
  if (!appData) return [];
4282
4348
  const npmBin = path.join(appData, "npm");
4283
4349
  let currentPath = "";
4284
4350
  try {
4285
- currentPath = execSync(
4286
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::GetEnvironmentVariable('Path', 'User')"`,
4287
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
4288
- ).trim();
4351
+ currentPath = getWindowsUserEnvVar("Path");
4289
4352
  } catch {
4290
4353
  currentPath = process.env.PATH || "";
4291
4354
  }
@@ -4294,70 +4357,40 @@ var require_shell = __commonJS({
4294
4357
  if (hasNpmBin) return [];
4295
4358
  const nextPath = [...parts, npmBin].join(";");
4296
4359
  try {
4297
- const escapedPath = nextPath.replace(/'/g, "''");
4298
- execSync(
4299
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::SetEnvironmentVariable('Path', '${escapedPath}', 'User')"`,
4300
- { stdio: "ignore" }
4301
- );
4360
+ setWindowsUserEnvVar("Path", nextPath);
4361
+ appendDirToProcessPath(npmBin);
4302
4362
  return ["[\u7528\u6237 PATH] %APPDATA%\\npm"];
4303
- } catch {
4304
- try {
4305
- const chalk = require("chalk");
4306
- console.warn(chalk.yellow(
4307
- ` \u26A0\uFE0F \u65E0\u6CD5\u81EA\u52A8\u66F4\u65B0 PATH\uFF0C\u8BF7\u624B\u52A8\u5C06\u4EE5\u4E0B\u8DEF\u5F84\u52A0\u5165\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF PATH\uFF1A
4308
- ${npmBin}`
4309
- ));
4310
- } catch {
4311
- }
4312
- return [];
4363
+ } catch (err) {
4364
+ return [`[\u7528\u6237 PATH \u5199\u5165\u5931\u8D25] %APPDATA%\\npm \u2014 ${err.message}`];
4313
4365
  }
4314
4366
  }
4315
4367
  __name(ensureWindowsUserPathHasNpmBin, "ensureWindowsUserPathHasNpmBin");
4316
4368
  function ensureWindowsUserPathHasLocalBin() {
4317
- if (process.platform !== "win32") return [];
4369
+ if (!isWindowsLike()) return [];
4318
4370
  const userProfile = process.env.USERPROFILE || os.homedir();
4319
4371
  if (!userProfile) return [];
4320
4372
  const localBin = path.join(userProfile, ".local", "bin");
4321
4373
  let currentPath = "";
4322
4374
  try {
4323
- currentPath = execSync(
4324
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::GetEnvironmentVariable('Path', 'User')"`,
4325
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
4326
- ).trim();
4375
+ currentPath = getWindowsUserEnvVar("Path");
4327
4376
  } catch {
4328
4377
  currentPath = process.env.PATH || "";
4329
4378
  }
4330
4379
  const parts = currentPath.split(";").map((item) => item.trim()).filter(Boolean);
4331
4380
  const hasLocalBin = parts.some((item) => item.toLowerCase() === localBin.toLowerCase());
4332
- const procParts = (process.env.PATH || "").split(";").map((p) => p.trim()).filter(Boolean);
4333
- const inProcessHasIt = procParts.some((item) => item.toLowerCase() === localBin.toLowerCase());
4334
- if (!inProcessHasIt) {
4335
- process.env.PATH = [...procParts, localBin].join(";");
4336
- }
4337
- if (hasLocalBin) return inProcessHasIt ? [] : ["[\u5F53\u524D\u8FDB\u7A0B PATH] %USERPROFILE%\\.local\\bin"];
4381
+ const inProcessHadIt = !appendDirToProcessPath(localBin);
4382
+ if (hasLocalBin) return inProcessHadIt ? [] : ["[\u5F53\u524D\u8FDB\u7A0B PATH] %USERPROFILE%\\.local\\bin"];
4338
4383
  const nextPath = [...parts, localBin].join(";");
4339
4384
  try {
4340
- const escapedPath = nextPath.replace(/'/g, "''");
4341
- execSync(
4342
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::SetEnvironmentVariable('Path', '${escapedPath}', 'User')"`,
4343
- { stdio: "ignore" }
4344
- );
4385
+ setWindowsUserEnvVar("Path", nextPath);
4345
4386
  return ["[\u7528\u6237 PATH] %USERPROFILE%\\.local\\bin"];
4346
- } catch {
4347
- try {
4348
- const chalk = require("chalk");
4349
- console.warn(chalk.yellow(
4350
- ` \u26A0\uFE0F \u65E0\u6CD5\u81EA\u52A8\u66F4\u65B0 PATH\uFF0C\u8BF7\u624B\u52A8\u5C06\u4EE5\u4E0B\u8DEF\u5F84\u52A0\u5165\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF PATH\uFF1A
4351
- ${localBin}`
4352
- ));
4353
- } catch {
4354
- }
4355
- return [];
4387
+ } catch (err) {
4388
+ return [`[\u7528\u6237 PATH \u5199\u5165\u5931\u8D25] %USERPROFILE%\\.local\\bin \u2014 ${err.message}`];
4356
4389
  }
4357
4390
  }
4358
4391
  __name(ensureWindowsUserPathHasLocalBin, "ensureWindowsUserPathHasLocalBin");
4359
4392
  function installWindowsCliShims() {
4360
- if (process.platform !== "win32") return [];
4393
+ if (!isWindowsLike()) return [];
4361
4394
  const appData = process.env.APPDATA;
4362
4395
  if (!appData) return [];
4363
4396
  const npmBin = path.join(appData, "npm");
@@ -4397,13 +4430,15 @@ var require_shell = __commonJS({
4397
4430
  }
4398
4431
  __name(installWindowsCliShims, "installWindowsCliShims");
4399
4432
  function writeEnvToShell(envVars) {
4400
- if (process.platform === "win32") {
4433
+ if (isWindowsLike()) {
4401
4434
  const written2 = [];
4402
4435
  for (const [k, v] of Object.entries(envVars)) {
4403
4436
  try {
4404
- execSync(`setx ${k} "${v}"`, { stdio: "ignore" });
4437
+ setWindowsUserEnvVar(k, v);
4438
+ process.env[k] = v;
4405
4439
  written2.push(`[\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF] ${k}`);
4406
- } catch {
4440
+ } catch (err) {
4441
+ written2.push(`[\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF\u5199\u5165\u5931\u8D25] ${k} \u2014 ${err.message}`);
4407
4442
  }
4408
4443
  }
4409
4444
  written2.push(...installWindowsCliShims());
@@ -4433,14 +4468,11 @@ var require_shell = __commonJS({
4433
4468
  }
4434
4469
  __name(writeEnvToShell, "writeEnvToShell");
4435
4470
  function removeWindowsUserEnvVars(keys = []) {
4436
- if (process.platform !== "win32") return [];
4471
+ if (!isWindowsLike()) return [];
4437
4472
  const removed = [];
4438
4473
  for (const key of keys) {
4439
4474
  try {
4440
- execSync(
4441
- `powershell.exe -NoProfile -Command "[Environment]::SetEnvironmentVariable('${key}', $null, 'User')"`,
4442
- { stdio: "ignore" }
4443
- );
4475
+ setWindowsUserEnvVar(key, null);
4444
4476
  delete process.env[key];
4445
4477
  removed.push(`[\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF] ${key}`);
4446
4478
  } catch {
@@ -4459,7 +4491,7 @@ var require_shell = __commonJS({
4459
4491
  "HOLYSHEEP_API_KEY",
4460
4492
  ...extraKeys
4461
4493
  ];
4462
- if (process.platform === "win32") {
4494
+ if (isWindowsLike()) {
4463
4495
  return removeWindowsUserEnvVars(HS_KEYS);
4464
4496
  }
4465
4497
  const files = getShellRcFiles();
@@ -4489,7 +4521,17 @@ var require_shell = __commonJS({
4489
4521
  ensureWindowsUserPathHasNpmBin,
4490
4522
  ensureWindowsUserPathHasLocalBin,
4491
4523
  installWindowsCliShims,
4492
- removeWindowsUserEnvVars
4524
+ removeWindowsUserEnvVars,
4525
+ _private: {
4526
+ runPowerShellUserEnvScript,
4527
+ getWindowsUserEnvVar,
4528
+ setWindowsUserEnvVar,
4529
+ setExecFileSyncForTest(runner) {
4530
+ execFileSyncOverride = runner || null;
4531
+ },
4532
+ quotePowerShellString,
4533
+ appendDirToProcessPath
4534
+ }
4493
4535
  };
4494
4536
  }
4495
4537
  });
package/dist/index.html CHANGED
@@ -1120,7 +1120,7 @@ function renderTools() {
1120
1120
  <div class="panel-header">
1121
1121
  <div>
1122
1122
  <h2>CLI 管理</h2>
1123
- <p>统一管理本地 AI CLI、MCP 与 VS Code Claude 代理;代理作为 Claude Code 的子状态展示。</p>
1123
+ <p>统一管理本地 AI CLI、MCP 与 VS Code Claude 代理;Claude Code 未安装时先安装 CLI,再启动代理。</p>
1124
1124
  </div>
1125
1125
  </div>
1126
1126
  <div class="panel-body tool-grid">
@@ -1136,12 +1136,11 @@ function renderTools() {
1136
1136
  </div>
1137
1137
  </div>
1138
1138
  <div class="inline-actions">
1139
- <button class="btn" onclick="launchTool('${tool.id}')">Launch</button>
1140
- <button class="btn primary" onclick="configureTool('${tool.id}')">${tool.configured ? 'Reconfigure' : 'Configure'}</button>
1139
+ ${renderToolActions(tool)}
1141
1140
  </div>
1142
1141
  </div>
1143
1142
  <div class="tool-hint">${esc(tool.hint || 'No additional hint')}</div>
1144
- ${tool.id === 'claude-code' ? renderClaudeProxySubstatus(tool.vsCodeProxy) : ''}
1143
+ ${tool.id === 'claude-code' ? renderClaudeProxySubstatus(tool) : ''}
1145
1144
  </div>
1146
1145
  `).join('')}
1147
1146
  </div>
@@ -1149,19 +1148,40 @@ function renderTools() {
1149
1148
  `
1150
1149
  }
1151
1150
 
1152
- function renderClaudeProxySubstatus(proxy) {
1153
- const running = !!proxy?.running
1151
+ function renderToolActions(tool) {
1152
+ if (tool.id === 'claude-code' && tool.requiresInstall) {
1153
+ return `<button class="btn primary" onclick="installTool('claude-code')">${esc(tool.installLabel || '安装 Claude Code CLI')}</button>`
1154
+ }
1155
+ const launchButton = tool.launchCmd ? `<button class="btn" onclick="launchTool('${tool.id}')">Launch</button>` : ''
1156
+ const configureButton = tool.installed ? `<button class="btn primary" onclick="configureTool('${tool.id}')">${tool.configured ? 'Reconfigure' : 'Configure'}</button>` : ''
1157
+ const installButton = !tool.installed && tool.canAutoInstall ? `<button class="btn primary" onclick="installTool('${tool.id}')">Install</button>` : ''
1158
+ return `${installButton}${launchButton}${configureButton}` || '<span class="task-meta">Manual setup required</span>'
1159
+ }
1160
+
1161
+ function renderClaudeProxySubstatus(tool) {
1162
+ if (tool?.requiresInstall || !tool?.installed || !tool?.vsCodeProxy) {
1163
+ return `
1164
+ <div class="tool-substatus install-first" data-requires-install="true">
1165
+ <div>
1166
+ <div class="tool-substatus-title">先安装 Claude Code CLI</div>
1167
+ <div class="tool-substatus-meta">${esc(tool?.installHint || '不存在单独的 vscode-claudecode 插件;VS Code 里使用官方 Claude Code 扩展 + HolySheep 本地代理。')}</div>
1168
+ </div>
1169
+ </div>
1170
+ `
1171
+ }
1172
+ const proxy = tool.vsCodeProxy
1173
+ const running = !!proxy.running
1154
1174
  const meta = running
1155
1175
  ? `127.0.0.1:${proxy.port || '?'} · PID ${proxy.pid || '?'}`
1156
1176
  : '未启动 · Windows 启动后需要完全退出 Code.exe 再重开 VS Code'
1157
1177
  return `
1158
1178
  <div class="tool-substatus">
1159
1179
  <div>
1160
- <div class="tool-substatus-title">VS Code Claude 代理</div>
1180
+ <div class="tool-substatus-title">${esc(proxy.label || 'VS Code Claude 代理(hs claude-proxy)')}</div>
1161
1181
  <div class="tool-substatus-meta">${esc(meta)}</div>
1162
1182
  </div>
1163
1183
  <div class="inline-actions">
1164
- <button class="btn ${running ? '' : 'primary'}" onclick="startClaudeProxy()">${running ? '重新校准' : '启动代理'}</button>
1184
+ <button class="btn ${running ? '' : 'primary'}" onclick="startClaudeProxy()">${running ? '重新校准' : '启动 VS Code 代理'}</button>
1165
1185
  ${running ? '<button class="btn danger" onclick="stopClaudeProxy()">停止</button>' : ''}
1166
1186
  </div>
1167
1187
  </div>
@@ -1537,6 +1557,10 @@ async function consumeSse(path, body, title) {
1537
1557
  await postMaybeSse(path, body, title)
1538
1558
  }
1539
1559
 
1560
+ function installTool(toolId) {
1561
+ consumeSse('tool/install', { toolId }, `Install ${toolNames[toolId] || toolId}`).then(() => refreshWorkspace(false))
1562
+ }
1563
+
1540
1564
  function configureTool(toolId) {
1541
1565
  consumeSse('tool/configure', { toolId }, `Configure ${toolNames[toolId] || toolId}`).then(() => refreshWorkspace(false))
1542
1566
  }
package/dist/index.js CHANGED
@@ -12,11 +12,11 @@ var require_package = __commonJS({
12
12
  "package.json"(exports2, module2) {
13
13
  module2.exports = {
14
14
  name: "@simonyea/holysheep-cli",
15
- version: "2.1.70",
15
+ version: "2.1.72",
16
16
  description: "Claude Code/Cursor/Cline API relay for China \u2014 \xA51=$1, WeChat/Alipay payment, no credit card, no VPN. One command setup for all AI coding tools.",
17
17
  scripts: {
18
18
  build: "node scripts/build.mjs",
19
- test: "node tests/droid.test.js && node tests/workspace-store.test.js && node tests/runtime-stale-upgrade.test.js && node tests/hermes.test.js && node tests/preflight.test.js && node tests/opencode-auth-purge.test.js && node tests/shell-winpath.test.js && node tests/openclaw-atomic-write.test.js && node tests/openclaw-disable-auth-direct.test.js && node tests/opencode-default-model.test.js && node tests/paths-bundled.test.js && node tests/aionui-runtime-resources.test.js && node tests/aionui-wrapper-claude-proxy.test.js && node tests/aionui-wrapper-probe.test.js && node tests/aionui-wrapper-proxy-integration.test.js && node tests/aionui-wrapper-all-clis-autoconf.test.js && node tests/aionui-wrapper-env-signal.test.js && node tests/aionui-wrapper-csp-rewrite.test.js && node tests/aionui-wrapper-version-status.test.js && node tests/claude-proxy-daemon.test.js && node tests/claude-proxy-vscode-settings.test.js && node tests/claude-proxy-vscode-env.test.js && node tests/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js && node tests/claude-code-configure-preserve.test.js && node tests/webui-claude-proxy-virtual-tool.test.js && node tests/webui-claude-proxy-ui.test.js && node tests/webui-response-consumer.test.js",
19
+ test: "node tests/droid.test.js && node tests/workspace-store.test.js && node tests/runtime-stale-upgrade.test.js && node tests/hermes.test.js && node tests/preflight.test.js && node tests/opencode-auth-purge.test.js && node tests/shell-winpath.test.js && node tests/openclaw-atomic-write.test.js && node tests/openclaw-disable-auth-direct.test.js && node tests/opencode-default-model.test.js && node tests/paths-bundled.test.js && node tests/aionui-runtime-resources.test.js && node tests/aionui-wrapper-claude-proxy.test.js && node tests/aionui-wrapper-probe.test.js && node tests/aionui-wrapper-proxy-integration.test.js && node tests/aionui-wrapper-all-clis-autoconf.test.js && node tests/aionui-wrapper-env-signal.test.js && node tests/aionui-wrapper-csp-rewrite.test.js && node tests/aionui-wrapper-version-status.test.js && node tests/claude-proxy-daemon.test.js && node tests/claude-proxy-vscode-settings.test.js && node tests/claude-proxy-vscode-env.test.js && node tests/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js && node tests/claude-code-configure-preserve.test.js && node tests/webui-claude-proxy-virtual-tool.test.js && node tests/webui-claude-proxy-ui.test.js && node tests/webui-response-consumer.test.js && node tests/webui-claude-proxy-install-gating.test.js && node tests/claude-code-windows-local-bin.test.js",
20
20
  prepublishOnly: "npm run build && npm test && node scripts/check-tarball-size.js"
21
21
  },
22
22
  keywords: [
@@ -336,13 +336,59 @@ var require_shell = __commonJS({
336
336
  var fs = require("fs");
337
337
  var path = require("path");
338
338
  var os = require("os");
339
- var { execSync } = require("child_process");
339
+ var { execSync, execFileSync } = require("child_process");
340
340
  var pkg2 = require_package();
341
341
  var MARKER_START = "# >>> holysheep-cli managed >>>";
342
342
  var MARKER_END = "# <<< holysheep-cli managed <<<";
343
+ function isWindowsLike() {
344
+ return process.platform === "win32" || process.env.HOLYSHEEP_TEST_PLATFORM === "win32";
345
+ }
346
+ __name(isWindowsLike, "isWindowsLike");
347
+ var execFileSyncOverride = null;
348
+ function quotePowerShellString(value) {
349
+ return `'${String(value).replace(/'/g, "''")}'`;
350
+ }
351
+ __name(quotePowerShellString, "quotePowerShellString");
352
+ function runPowerShellUserEnvScript(script, options = {}) {
353
+ const runner = execFileSyncOverride || execFileSync;
354
+ try {
355
+ return runner("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], {
356
+ encoding: "utf8",
357
+ windowsHide: true,
358
+ timeout: options.timeout || 5e3
359
+ });
360
+ } catch (err) {
361
+ const stderr = err && err.stderr ? String(err.stderr).trim() : "";
362
+ const stdout = err && err.stdout ? String(err.stdout).trim() : "";
363
+ const detail = stderr || stdout || err && err.message || "unknown error";
364
+ throw new Error(`PowerShell \u7528\u6237\u73AF\u5883\u53D8\u91CF\u64CD\u4F5C\u5931\u8D25: ${detail}`);
365
+ }
366
+ }
367
+ __name(runPowerShellUserEnvScript, "runPowerShellUserEnvScript");
368
+ function getWindowsUserEnvVar(name) {
369
+ return runPowerShellUserEnvScript(`[Environment]::GetEnvironmentVariable(${quotePowerShellString(name)}, 'User')`).trim();
370
+ }
371
+ __name(getWindowsUserEnvVar, "getWindowsUserEnvVar");
372
+ function setWindowsUserEnvVar(name, value) {
373
+ runPowerShellUserEnvScript(`[Environment]::SetEnvironmentVariable(${quotePowerShellString(name)}, ${value === null || value === void 0 ? "$null" : quotePowerShellString(value)}, 'User')`);
374
+ }
375
+ __name(setWindowsUserEnvVar, "setWindowsUserEnvVar");
376
+ function splitWindowsPath(value) {
377
+ return String(value || "").split(";").map((item) => item.trim()).filter(Boolean);
378
+ }
379
+ __name(splitWindowsPath, "splitWindowsPath");
380
+ function appendDirToProcessPath(dir) {
381
+ const parts = splitWindowsPath(process.env.PATH);
382
+ if (!parts.some((item) => item.toLowerCase() === dir.toLowerCase())) {
383
+ process.env.PATH = [...parts, dir].join(";");
384
+ return true;
385
+ }
386
+ return false;
387
+ }
388
+ __name(appendDirToProcessPath, "appendDirToProcessPath");
343
389
  function getShellRcFiles() {
344
390
  const home = os.homedir();
345
- if (process.platform === "win32") return [];
391
+ if (isWindowsLike()) return [];
346
392
  const shell = process.env.SHELL || "";
347
393
  const candidates = [];
348
394
  if (shell.includes("zsh")) candidates.push(path.join(home, ".zshrc"));
@@ -388,16 +434,13 @@ var require_shell = __commonJS({
388
434
  }
389
435
  __name(buildEnvBlock, "buildEnvBlock");
390
436
  function ensureWindowsUserPathHasNpmBin() {
391
- if (process.platform !== "win32") return [];
437
+ if (!isWindowsLike()) return [];
392
438
  const appData = process.env.APPDATA;
393
439
  if (!appData) return [];
394
440
  const npmBin = path.join(appData, "npm");
395
441
  let currentPath = "";
396
442
  try {
397
- currentPath = execSync(
398
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::GetEnvironmentVariable('Path', 'User')"`,
399
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
400
- ).trim();
443
+ currentPath = getWindowsUserEnvVar("Path");
401
444
  } catch {
402
445
  currentPath = process.env.PATH || "";
403
446
  }
@@ -406,70 +449,40 @@ var require_shell = __commonJS({
406
449
  if (hasNpmBin) return [];
407
450
  const nextPath = [...parts, npmBin].join(";");
408
451
  try {
409
- const escapedPath = nextPath.replace(/'/g, "''");
410
- execSync(
411
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::SetEnvironmentVariable('Path', '${escapedPath}', 'User')"`,
412
- { stdio: "ignore" }
413
- );
452
+ setWindowsUserEnvVar("Path", nextPath);
453
+ appendDirToProcessPath(npmBin);
414
454
  return ["[\u7528\u6237 PATH] %APPDATA%\\npm"];
415
- } catch {
416
- try {
417
- const chalk2 = require("chalk");
418
- console.warn(chalk2.yellow(
419
- ` \u26A0\uFE0F \u65E0\u6CD5\u81EA\u52A8\u66F4\u65B0 PATH\uFF0C\u8BF7\u624B\u52A8\u5C06\u4EE5\u4E0B\u8DEF\u5F84\u52A0\u5165\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF PATH\uFF1A
420
- ${npmBin}`
421
- ));
422
- } catch {
423
- }
424
- return [];
455
+ } catch (err) {
456
+ return [`[\u7528\u6237 PATH \u5199\u5165\u5931\u8D25] %APPDATA%\\npm \u2014 ${err.message}`];
425
457
  }
426
458
  }
427
459
  __name(ensureWindowsUserPathHasNpmBin, "ensureWindowsUserPathHasNpmBin");
428
460
  function ensureWindowsUserPathHasLocalBin() {
429
- if (process.platform !== "win32") return [];
461
+ if (!isWindowsLike()) return [];
430
462
  const userProfile = process.env.USERPROFILE || os.homedir();
431
463
  if (!userProfile) return [];
432
464
  const localBin = path.join(userProfile, ".local", "bin");
433
465
  let currentPath = "";
434
466
  try {
435
- currentPath = execSync(
436
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::GetEnvironmentVariable('Path', 'User')"`,
437
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
438
- ).trim();
467
+ currentPath = getWindowsUserEnvVar("Path");
439
468
  } catch {
440
469
  currentPath = process.env.PATH || "";
441
470
  }
442
471
  const parts = currentPath.split(";").map((item) => item.trim()).filter(Boolean);
443
472
  const hasLocalBin = parts.some((item) => item.toLowerCase() === localBin.toLowerCase());
444
- const procParts = (process.env.PATH || "").split(";").map((p) => p.trim()).filter(Boolean);
445
- const inProcessHasIt = procParts.some((item) => item.toLowerCase() === localBin.toLowerCase());
446
- if (!inProcessHasIt) {
447
- process.env.PATH = [...procParts, localBin].join(";");
448
- }
449
- if (hasLocalBin) return inProcessHasIt ? [] : ["[\u5F53\u524D\u8FDB\u7A0B PATH] %USERPROFILE%\\.local\\bin"];
473
+ const inProcessHadIt = !appendDirToProcessPath(localBin);
474
+ if (hasLocalBin) return inProcessHadIt ? [] : ["[\u5F53\u524D\u8FDB\u7A0B PATH] %USERPROFILE%\\.local\\bin"];
450
475
  const nextPath = [...parts, localBin].join(";");
451
476
  try {
452
- const escapedPath = nextPath.replace(/'/g, "''");
453
- execSync(
454
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::SetEnvironmentVariable('Path', '${escapedPath}', 'User')"`,
455
- { stdio: "ignore" }
456
- );
477
+ setWindowsUserEnvVar("Path", nextPath);
457
478
  return ["[\u7528\u6237 PATH] %USERPROFILE%\\.local\\bin"];
458
- } catch {
459
- try {
460
- const chalk2 = require("chalk");
461
- console.warn(chalk2.yellow(
462
- ` \u26A0\uFE0F \u65E0\u6CD5\u81EA\u52A8\u66F4\u65B0 PATH\uFF0C\u8BF7\u624B\u52A8\u5C06\u4EE5\u4E0B\u8DEF\u5F84\u52A0\u5165\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF PATH\uFF1A
463
- ${localBin}`
464
- ));
465
- } catch {
466
- }
467
- return [];
479
+ } catch (err) {
480
+ return [`[\u7528\u6237 PATH \u5199\u5165\u5931\u8D25] %USERPROFILE%\\.local\\bin \u2014 ${err.message}`];
468
481
  }
469
482
  }
470
483
  __name(ensureWindowsUserPathHasLocalBin, "ensureWindowsUserPathHasLocalBin");
471
484
  function installWindowsCliShims() {
472
- if (process.platform !== "win32") return [];
485
+ if (!isWindowsLike()) return [];
473
486
  const appData = process.env.APPDATA;
474
487
  if (!appData) return [];
475
488
  const npmBin = path.join(appData, "npm");
@@ -509,13 +522,15 @@ var require_shell = __commonJS({
509
522
  }
510
523
  __name(installWindowsCliShims, "installWindowsCliShims");
511
524
  function writeEnvToShell(envVars) {
512
- if (process.platform === "win32") {
525
+ if (isWindowsLike()) {
513
526
  const written2 = [];
514
527
  for (const [k, v] of Object.entries(envVars)) {
515
528
  try {
516
- execSync(`setx ${k} "${v}"`, { stdio: "ignore" });
529
+ setWindowsUserEnvVar(k, v);
530
+ process.env[k] = v;
517
531
  written2.push(`[\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF] ${k}`);
518
- } catch {
532
+ } catch (err) {
533
+ written2.push(`[\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF\u5199\u5165\u5931\u8D25] ${k} \u2014 ${err.message}`);
519
534
  }
520
535
  }
521
536
  written2.push(...installWindowsCliShims());
@@ -545,14 +560,11 @@ var require_shell = __commonJS({
545
560
  }
546
561
  __name(writeEnvToShell, "writeEnvToShell");
547
562
  function removeWindowsUserEnvVars(keys = []) {
548
- if (process.platform !== "win32") return [];
563
+ if (!isWindowsLike()) return [];
549
564
  const removed = [];
550
565
  for (const key of keys) {
551
566
  try {
552
- execSync(
553
- `powershell.exe -NoProfile -Command "[Environment]::SetEnvironmentVariable('${key}', $null, 'User')"`,
554
- { stdio: "ignore" }
555
- );
567
+ setWindowsUserEnvVar(key, null);
556
568
  delete process.env[key];
557
569
  removed.push(`[\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF] ${key}`);
558
570
  } catch {
@@ -571,7 +583,7 @@ var require_shell = __commonJS({
571
583
  "HOLYSHEEP_API_KEY",
572
584
  ...extraKeys
573
585
  ];
574
- if (process.platform === "win32") {
586
+ if (isWindowsLike()) {
575
587
  return removeWindowsUserEnvVars(HS_KEYS);
576
588
  }
577
589
  const files = getShellRcFiles();
@@ -601,7 +613,17 @@ var require_shell = __commonJS({
601
613
  ensureWindowsUserPathHasNpmBin,
602
614
  ensureWindowsUserPathHasLocalBin,
603
615
  installWindowsCliShims,
604
- removeWindowsUserEnvVars
616
+ removeWindowsUserEnvVars,
617
+ _private: {
618
+ runPowerShellUserEnvScript,
619
+ getWindowsUserEnvVar,
620
+ setWindowsUserEnvVar,
621
+ setExecFileSyncForTest(runner) {
622
+ execFileSyncOverride = runner || null;
623
+ },
624
+ quotePowerShellString,
625
+ appendDirToProcessPath
626
+ }
605
627
  };
606
628
  }
607
629
  });
@@ -611,7 +633,7 @@ var require_which = __commonJS({
611
633
  "src/utils/which.js"(exports2, module2) {
612
634
  var path = require("path");
613
635
  var fs = require("fs");
614
- var { exec, execSync } = require("child_process");
636
+ var { exec, execSync, execFileSync } = require("child_process");
615
637
  function canRun(command, options = {}) {
616
638
  try {
617
639
  execSync(command, { stdio: "ignore", ...options });
@@ -629,8 +651,22 @@ var require_which = __commonJS({
629
651
  });
630
652
  }
631
653
  __name(canRunAsync, "canRunAsync");
654
+ function isWindowsLike() {
655
+ return process.platform === "win32" || process.env.HOLYSHEEP_TEST_PLATFORM === "win32";
656
+ }
657
+ __name(isWindowsLike, "isWindowsLike");
658
+ function appendDirToProcessPath(dir) {
659
+ if (!dir) return false;
660
+ const delimiter = isWindowsLike() ? ";" : path.delimiter;
661
+ const parts = (process.env.PATH || "").split(delimiter).map((item) => item.trim()).filter(Boolean);
662
+ const hasDir = parts.some((item) => item.toLowerCase() === dir.toLowerCase());
663
+ if (hasDir) return false;
664
+ process.env.PATH = [...parts, dir].join(delimiter);
665
+ return true;
666
+ }
667
+ __name(appendDirToProcessPath, "appendDirToProcessPath");
632
668
  function getWindowsKnownPaths(cmd) {
633
- if (process.platform !== "win32") return [];
669
+ if (!isWindowsLike()) return [];
634
670
  const home = process.env.USERPROFILE || process.env.HOME || "";
635
671
  const appData = process.env.APPDATA || "";
636
672
  const localAppData = process.env.LOCALAPPDATA || "";
@@ -651,7 +687,10 @@ var require_which = __commonJS({
651
687
  function checkKnownPathsSync(cmd) {
652
688
  for (const p of getWindowsKnownPaths(cmd)) {
653
689
  try {
654
- if (fs.existsSync(p)) return p;
690
+ if (fs.existsSync(p)) {
691
+ appendDirToProcessPath(path.dirname(p));
692
+ return p;
693
+ }
655
694
  } catch {
656
695
  }
657
696
  }
@@ -660,14 +699,15 @@ var require_which = __commonJS({
660
699
  __name(checkKnownPathsSync, "checkKnownPathsSync");
661
700
  var _userPathRefreshedAt = 0;
662
701
  function refreshWindowsUserPath() {
663
- if (process.platform !== "win32") return;
702
+ if (!isWindowsLike()) return;
664
703
  const now = Date.now();
665
704
  if (now - _userPathRefreshedAt < 5e3) return;
666
705
  _userPathRefreshedAt = now;
667
706
  try {
668
- const out = execSync(
669
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::GetEnvironmentVariable('Path', 'User')"`,
670
- { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 }
707
+ const out = execFileSync(
708
+ "powershell.exe",
709
+ ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", "[Environment]::GetEnvironmentVariable('Path', 'User')"],
710
+ { encoding: "utf8", windowsHide: true, timeout: 5e3 }
671
711
  ).trim();
672
712
  if (!out) return;
673
713
  const userParts = out.split(";").map((s) => s.trim()).filter(Boolean);
@@ -688,7 +728,7 @@ var require_which = __commonJS({
688
728
  }
689
729
  __name(refreshWindowsUserPath, "refreshWindowsUserPath");
690
730
  function commandExists2(cmd) {
691
- if (process.platform === "win32") {
731
+ if (isWindowsLike()) {
692
732
  const variants = [cmd, `${cmd}.cmd`, `${cmd}.exe`, `${cmd}.bat`];
693
733
  for (const variant of variants) {
694
734
  if (canRun(`where ${variant}`)) return true;
@@ -708,7 +748,7 @@ var require_which = __commonJS({
708
748
  }
709
749
  __name(commandExists2, "commandExists");
710
750
  async function commandExistsAsync(cmd) {
711
- if (process.platform === "win32") {
751
+ if (isWindowsLike()) {
712
752
  const variants = [cmd, `${cmd}.cmd`, `${cmd}.exe`, `${cmd}.bat`];
713
753
  for (const variant of variants) {
714
754
  if (await canRunAsync(`where ${variant}`)) return true;
@@ -732,7 +772,9 @@ var require_which = __commonJS({
732
772
  commandExistsAsync,
733
773
  // test-only exports
734
774
  _getWindowsKnownPaths: getWindowsKnownPaths,
735
- _refreshWindowsUserPath: refreshWindowsUserPath
775
+ _refreshWindowsUserPath: refreshWindowsUserPath,
776
+ _checkKnownPathsSync: checkKnownPathsSync,
777
+ _appendDirToProcessPath: appendDirToProcessPath
736
778
  };
737
779
  }
738
780
  });
@@ -7685,29 +7727,37 @@ var require_server = __commonJS({
7685
7727
  async function buildToolSummary(tool) {
7686
7728
  var _a, _b;
7687
7729
  const installed = await detectToolInstalled(tool);
7730
+ const isClaudeCode = tool.id === "claude-code";
7688
7731
  const summary = {
7689
7732
  id: tool.id,
7690
- name: tool.name,
7733
+ name: isClaudeCode ? "Claude Code CLI" : tool.name,
7691
7734
  installed,
7692
7735
  configured: installed ? ((_a = tool.isConfigured) == null ? void 0 : _a.call(tool)) || false : false,
7693
7736
  version: installed ? await getVersionAsync(tool) : null,
7694
7737
  installCmd: tool.installCmd,
7695
- hint: tool.hint || null,
7696
- launchCmd: tool.launchCmd || null,
7738
+ hint: isClaudeCode ? "\u5B98\u65B9 Claude Code CLI\uFF1BVS Code \u91CC\u4F7F\u7528\u5B98\u65B9 Claude Code \u6269\u5C55 + HolySheep \u672C\u5730\u4EE3\u7406\uFF0C\u4E0D\u5B58\u5728\u5355\u72EC\u7684 vscode-claudecode \u63D2\u4EF6\u3002" : tool.hint || null,
7739
+ launchCmd: installed ? tool.launchCmd || null : null,
7697
7740
  canAutoInstall: !!AUTO_INSTALL[tool.id],
7698
- canUpgrade: !!UPGRADABLE_TOOLS.find((item) => item.id === tool.id),
7741
+ canUpgrade: installed && !!UPGRADABLE_TOOLS.find((item) => item.id === tool.id),
7699
7742
  npmPkg: ((_b = UPGRADABLE_TOOLS.find((item) => item.id === tool.id)) == null ? void 0 : _b.npmPkg) || null
7700
7743
  };
7701
- if (tool.id === "claude-code") {
7702
- const proxyState = isClaudeProxyRunning();
7703
- summary.vsCodeProxy = {
7704
- running: proxyState.running,
7705
- configured: proxyState.running,
7706
- port: proxyState.port || null,
7707
- pid: proxyState.pid || null,
7708
- launchCmd: "hs claude-proxy --daemon",
7709
- hint: proxyState.running ? `VS Code \u4EE3\u7406\u8FD0\u884C\u4E2D\uFF1A127.0.0.1:${proxyState.port}` : "VS Code Claude Code \u9700\u8981\u542F\u52A8\u672C\u5730\u4EE3\u7406\uFF1B\u542F\u52A8\u540E\u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u5F00 VS Code\u3002"
7710
- };
7744
+ if (isClaudeCode) {
7745
+ summary.requiresInstall = !installed;
7746
+ summary.installLabel = "\u5B89\u88C5 Claude Code CLI";
7747
+ summary.installHint = process.platform === "win32" ? "Windows \u5C06\u8FD0\u884C\u5B98\u65B9\u5B89\u88C5\u811A\u672C\uFF1Airm https://claude.ai/install.ps1 | iex\u3002\u5B89\u88C5\u540E\u8BF7\u91CD\u65B0\u6253\u5F00 PowerShell/VS Code\u3002" : "\u5B89\u88C5\u5B98\u65B9 Claude Code CLI \u540E\uFF0C\u624D\u80FD\u542F\u52A8 VS Code Claude \u4EE3\u7406\uFF08hs claude-proxy\uFF09\u3002";
7748
+ if (installed) {
7749
+ const proxyState = isClaudeProxyRunning();
7750
+ summary.vsCodeProxy = {
7751
+ running: proxyState.running,
7752
+ configured: proxyState.running,
7753
+ requiresInstall: false,
7754
+ port: proxyState.port || null,
7755
+ pid: proxyState.pid || null,
7756
+ launchCmd: "hs claude-proxy --daemon",
7757
+ label: "VS Code Claude \u4EE3\u7406\uFF08hs claude-proxy\uFF09",
7758
+ hint: proxyState.running ? `VS Code Claude \u4EE3\u7406\u8FD0\u884C\u4E2D\uFF1A127.0.0.1:${proxyState.port}` : "\u5DF2\u5B89\u88C5 Claude Code CLI\u3002\u542F\u52A8 VS Code Claude \u4EE3\u7406\u540E\uFF0C\u8BF7\u5B8C\u5168\u9000\u51FA\u6240\u6709 Code.exe\uFF0C\u518D\u91CD\u65B0\u6253\u5F00 VS Code\u3002"
7759
+ };
7760
+ }
7711
7761
  }
7712
7762
  return summary;
7713
7763
  }
@@ -9742,10 +9792,14 @@ var require_aionui_wrapper = __commonJS({
9742
9792
  // [v2.1.63] \u542F\u52A8 button \u2014 installed + has launchCmd (covers OpenClaw etc.)
9743
9793
  if(t.installed&&t.launchCmd)btns+='<button data-act="launch" data-id="'+t.id+'" style="'+btnSty+';background:#f59e0b">\u542F\u52A8</button>';
9744
9794
  var proxy='';
9745
- if(t.id==='claude-code'&&t.vsCodeProxy){
9746
- var p=t.vsCodeProxy;
9747
- var pText=p.running?('\u8FD0\u884C\u4E2D 127.0.0.1:'+p.port+' \xB7 PID '+p.pid):'\u672A\u542F\u52A8\uFF08Windows \u542F\u52A8\u540E\u9700\u5B8C\u5168\u9000\u51FA Code.exe \u518D\u91CD\u5F00 VS Code\uFF09';
9748
- proxy='<div style="margin-top:6px;padding:6px 8px;background:#fff7ed;border:1px solid #fed7aa;border-radius:6px;font-size:11px;color:#9a3412">VS Code \u4EE3\u7406\uFF1A'+pText+' <button data-act="launch" data-id="claude-proxy" style="'+btnSty+';background:#f59e0b">'+(p.running?'\u91CD\u65B0\u6821\u51C6':'\u542F\u52A8\u4EE3\u7406')+'</button>'+(p.running?' <button data-act="reset" data-id="claude-proxy" style="'+btnSty+';background:#dc2626">\u505C\u6B62</button>':'')+'</div>';
9795
+ if(t.id==='claude-code'){
9796
+ if(t.requiresInstall||!t.installed||!t.vsCodeProxy){
9797
+ proxy='<div style="margin-top:6px;padding:6px 8px;background:#eff6ff;border:1px solid #bfdbfe;border-radius:6px;font-size:11px;color:#1d4ed8" data-requires-install="true">\u5148\u5B89\u88C5 Claude Code CLI\uFF1B\u4E0D\u5B58\u5728\u5355\u72EC\u7684 vscode-claudecode \u63D2\u4EF6\uFF0CVS Code \u91CC\u4F7F\u7528\u5B98\u65B9 Claude Code \u6269\u5C55 + HolySheep \u672C\u5730\u4EE3\u7406\u3002</div>';
9798
+ }else{
9799
+ var p=t.vsCodeProxy;
9800
+ var pText=p.running?('\u8FD0\u884C\u4E2D 127.0.0.1:'+p.port+' \xB7 PID '+p.pid):'\u672A\u542F\u52A8\uFF08Windows \u542F\u52A8\u540E\u9700\u5B8C\u5168\u9000\u51FA Code.exe \u518D\u91CD\u5F00 VS Code\uFF09';
9801
+ proxy='<div style="margin-top:6px;padding:6px 8px;background:#fff7ed;border:1px solid #fed7aa;border-radius:6px;font-size:11px;color:#9a3412">VS Code Claude \u4EE3\u7406\uFF08hs claude-proxy\uFF09\uFF1A'+pText+' <button data-act="launch" data-id="claude-proxy" style="'+btnSty+';background:#f59e0b">'+(p.running?'\u91CD\u65B0\u6821\u51C6':'\u542F\u52A8 VS Code \u4EE3\u7406')+'</button>'+(p.running?' <button data-act="reset" data-id="claude-proxy" style="'+btnSty+';background:#dc2626">\u505C\u6B62</button>':'')+'</div>';
9802
+ }
9749
9803
  }
9750
9804
  return '<div style="padding:8px 0;border-bottom:1px solid #eee"><div style="display:flex;justify-content:space-between;align-items:center"><div><b>'+t.name+'</b> '+statusBadge+' '+verText+'</div><div>'+btns+'</div></div>'+proxy+'</div>';
9751
9805
  }).join('')+'<div style="padding-top:10px"><button id="hs-cli-upgrade-all" style="width:100%;padding:8px;background:#16a34a;color:#fff;border:0;border-radius:4px;cursor:pointer;font-weight:600">\u4E00\u952E\u5347\u7EA7\u5168\u90E8\u5DF2\u5B89\u88C5\u7684 CLI</button></div>';
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@simonyea/holysheep-cli",
3
- "version": "2.1.70",
3
+ "version": "2.1.72",
4
4
  "description": "Claude Code/Cursor/Cline API relay for China — ¥1=$1, WeChat/Alipay payment, no credit card, no VPN. One command setup for all AI coding tools.",
5
5
  "scripts": {
6
6
  "build": "node scripts/build.mjs",
7
- "test": "node tests/droid.test.js && node tests/workspace-store.test.js && node tests/runtime-stale-upgrade.test.js && node tests/hermes.test.js && node tests/preflight.test.js && node tests/opencode-auth-purge.test.js && node tests/shell-winpath.test.js && node tests/openclaw-atomic-write.test.js && node tests/openclaw-disable-auth-direct.test.js && node tests/opencode-default-model.test.js && node tests/paths-bundled.test.js && node tests/aionui-runtime-resources.test.js && node tests/aionui-wrapper-claude-proxy.test.js && node tests/aionui-wrapper-probe.test.js && node tests/aionui-wrapper-proxy-integration.test.js && node tests/aionui-wrapper-all-clis-autoconf.test.js && node tests/aionui-wrapper-env-signal.test.js && node tests/aionui-wrapper-csp-rewrite.test.js && node tests/aionui-wrapper-version-status.test.js && node tests/claude-proxy-daemon.test.js && node tests/claude-proxy-vscode-settings.test.js && node tests/claude-proxy-vscode-env.test.js && node tests/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js && node tests/claude-code-configure-preserve.test.js && node tests/webui-claude-proxy-virtual-tool.test.js && node tests/webui-claude-proxy-ui.test.js && node tests/webui-response-consumer.test.js",
7
+ "test": "node tests/droid.test.js && node tests/workspace-store.test.js && node tests/runtime-stale-upgrade.test.js && node tests/hermes.test.js && node tests/preflight.test.js && node tests/opencode-auth-purge.test.js && node tests/shell-winpath.test.js && node tests/openclaw-atomic-write.test.js && node tests/openclaw-disable-auth-direct.test.js && node tests/opencode-default-model.test.js && node tests/paths-bundled.test.js && node tests/aionui-runtime-resources.test.js && node tests/aionui-wrapper-claude-proxy.test.js && node tests/aionui-wrapper-probe.test.js && node tests/aionui-wrapper-proxy-integration.test.js && node tests/aionui-wrapper-all-clis-autoconf.test.js && node tests/aionui-wrapper-env-signal.test.js && node tests/aionui-wrapper-csp-rewrite.test.js && node tests/aionui-wrapper-version-status.test.js && node tests/claude-proxy-daemon.test.js && node tests/claude-proxy-vscode-settings.test.js && node tests/claude-proxy-vscode-env.test.js && node tests/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js && node tests/claude-code-configure-preserve.test.js && node tests/webui-claude-proxy-virtual-tool.test.js && node tests/webui-claude-proxy-ui.test.js && node tests/webui-response-consumer.test.js && node tests/webui-claude-proxy-install-gating.test.js && node tests/claude-code-windows-local-bin.test.js",
8
8
  "prepublishOnly": "npm run build && npm test && node scripts/check-tarball-size.js"
9
9
  },
10
10
  "keywords": [