@simonyea/holysheep-cli 2.1.64 → 2.1.66

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.
@@ -73,7 +73,7 @@ var require_claude_process_proxy = __commonJS({
73
73
  var crypto = require("crypto");
74
74
  var { URL: URL2 } = require("url");
75
75
  var fetch = global.fetch || require("node-fetch");
76
- var HOLYSHEEP_DIR = path.join(os.homedir(), ".holysheep");
76
+ var HOLYSHEEP_DIR = process.env.HOLYSHEEP_DIR || path.join(os.homedir(), ".holysheep");
77
77
  var CONFIG_PATH = path.join(HOLYSHEEP_DIR, "claude-proxy.json");
78
78
  var DEFAULT_PROXY_PORT = 14556;
79
79
  var LOCAL_PING_PATH = "/__hs_local_ping__";
@@ -4099,11 +4099,11 @@ var require_package = __commonJS({
4099
4099
  "package.json"(exports2, module2) {
4100
4100
  module2.exports = {
4101
4101
  name: "@simonyea/holysheep-cli",
4102
- version: "2.1.64",
4102
+ version: "2.1.66",
4103
4103
  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.",
4104
4104
  scripts: {
4105
4105
  build: "node scripts/build.mjs",
4106
- 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/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js",
4106
+ 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/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js",
4107
4107
  prepublishOnly: "npm run build && npm test && node scripts/check-tarball-size.js"
4108
4108
  },
4109
4109
  keywords: [
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.64",
15
+ version: "2.1.66",
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/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.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/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js",
20
20
  prepublishOnly: "npm run build && npm test && node scripts/check-tarball-size.js"
21
21
  },
22
22
  keywords: [
@@ -750,7 +750,7 @@ var require_claude_process_proxy = __commonJS({
750
750
  var crypto = require("crypto");
751
751
  var { URL: URL2 } = require("url");
752
752
  var fetch = global.fetch || require("node-fetch");
753
- var HOLYSHEEP_DIR = path.join(os.homedir(), ".holysheep");
753
+ var HOLYSHEEP_DIR = process.env.HOLYSHEEP_DIR || path.join(os.homedir(), ".holysheep");
754
754
  var CONFIG_PATH = path.join(HOLYSHEEP_DIR, "claude-proxy.json");
755
755
  var DEFAULT_PROXY_PORT = 14556;
756
756
  var LOCAL_PING_PATH = "/__hs_local_ping__";
@@ -7063,145 +7063,6 @@ var require_workspace_runtime = __commonJS({
7063
7063
  }
7064
7064
  });
7065
7065
 
7066
- // src/commands/claude.js
7067
- var require_claude = __commonJS({
7068
- "src/commands/claude.js"(exports2, module2) {
7069
- "use strict";
7070
- var path = require("path");
7071
- var { spawn } = require("child_process");
7072
- var {
7073
- BASE_URL_ANTHROPIC,
7074
- BASE_URL_CLAUDE_RELAY,
7075
- getApiKey
7076
- } = require_config();
7077
- var {
7078
- closeSession,
7079
- getLocalProxyUrl,
7080
- startProcessProxy,
7081
- readConfig,
7082
- writeConfig
7083
- } = require_claude_process_proxy();
7084
- var claudeCodeTool = require_claude_code();
7085
- var { processProxyInjectPath } = require_paths();
7086
- var INJECT_PATH = processProxyInjectPath();
7087
- function appendNodeRequire(existingValue, requirePath) {
7088
- const nextFlag = `--require ${requirePath}`;
7089
- if (!existingValue) return nextFlag;
7090
- return existingValue.includes(nextFlag) ? existingValue : `${existingValue} ${nextFlag}`.trim();
7091
- }
7092
- __name(appendNodeRequire, "appendNodeRequire");
7093
- function mergeNoProxy(existingValue, extraHosts = []) {
7094
- const merged = /* @__PURE__ */ new Set();
7095
- for (const chunk of String(existingValue || "").split(",")) {
7096
- const value = chunk.trim();
7097
- if (value) merged.add(value);
7098
- }
7099
- for (const host of extraHosts) {
7100
- if (host) merged.add(host);
7101
- }
7102
- return Array.from(merged).join(",");
7103
- }
7104
- __name(mergeNoProxy, "mergeNoProxy");
7105
- function ensureClaudeProxyConfig(apiKey) {
7106
- const config = readConfig();
7107
- const next = claudeCodeTool.buildBridgeConfig(apiKey, BASE_URL_ANTHROPIC, {
7108
- ...config,
7109
- relayUrl: config.relayUrl || BASE_URL_CLAUDE_RELAY
7110
- });
7111
- const changed = JSON.stringify(next) !== JSON.stringify(config);
7112
- if (changed) {
7113
- writeConfig(next);
7114
- return next;
7115
- }
7116
- return config;
7117
- }
7118
- __name(ensureClaudeProxyConfig, "ensureClaudeProxyConfig");
7119
- async function runClaude(args = []) {
7120
- var _a;
7121
- const config = readConfig();
7122
- const apiKey = config.apiKey || getApiKey();
7123
- if (!apiKey) {
7124
- throw new Error("Missing API Key. Run hs setup first.");
7125
- }
7126
- try {
7127
- const { scheduleUpgradeBannerCheck } = require_version_check();
7128
- const pkg2 = require_package();
7129
- scheduleUpgradeBannerCheck({ localVersion: pkg2.version });
7130
- } catch {
7131
- }
7132
- ensureClaudeProxyConfig(apiKey);
7133
- const runtime = typeof claudeCodeTool.detectClaudeRuntime === "function" ? claudeCodeTool.detectClaudeRuntime() : { kind: "unknown", launchMode: "env-proxy" };
7134
- if (runtime.kind === "missing") {
7135
- const installCmd = process.platform === "win32" ? "irm https://claude.ai/install.ps1 | iex" : "curl -fsSL https://claude.ai/install.sh | bash";
7136
- const err = new Error(
7137
- `\u672A\u627E\u5230 claude \u547D\u4EE4 \u2014 Claude Code \u5C1A\u672A\u5B89\u88C5\u3002
7138
- \u8BF7\u5148\u8FD0\u884C: ${installCmd}
7139
- \u5B89\u88C5\u540E\u91CD\u5F00\u7EC8\u7AEF\u518D\u6267\u884C hs claude\u3002`
7140
- );
7141
- err.code = "CLAUDE_NOT_INSTALLED";
7142
- err.exitCode = 127;
7143
- throw err;
7144
- }
7145
- const settings = claudeCodeTool.readSettings();
7146
- if ((_a = settings.env) == null ? void 0 : _a.ANTHROPIC_BASE_URL) {
7147
- delete settings.env.ANTHROPIC_BASE_URL;
7148
- claudeCodeTool.writeSettings(settings);
7149
- }
7150
- const { server, port, sessionId } = await startProcessProxy({});
7151
- const proxyUrl = getLocalProxyUrl(port);
7152
- const launchMode = runtime.launchMode === "node-inject" ? "local-api + connect-fallback + node-inject" : "whole-process-proxy + local-api";
7153
- const env = {
7154
- ...process.env,
7155
- ANTHROPIC_API_KEY: void 0,
7156
- ANTHROPIC_AUTH_TOKEN: apiKey,
7157
- ANTHROPIC_BASE_URL: proxyUrl,
7158
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
7159
- HOLYSHEEP_CLAUDE_PROCESS_PROXY: "1",
7160
- HOLYSHEEP_CLAUDE_SESSION_ID: sessionId,
7161
- HS_PROXY_URL: proxyUrl,
7162
- HOLYSHEEP_CLAUDE_RUNTIME_KIND: runtime.kind || "unknown",
7163
- HOLYSHEEP_CLAUDE_LAUNCH_MODE: launchMode,
7164
- HTTP_PROXY: proxyUrl,
7165
- HTTPS_PROXY: proxyUrl,
7166
- ALL_PROXY: proxyUrl,
7167
- NO_PROXY: mergeNoProxy(process.env.NO_PROXY, ["127.0.0.1", "localhost"]),
7168
- ANTHROPIC_MAX_RETRIES: "0",
7169
- MAX_RETRIES: "0"
7170
- };
7171
- if (runtime.launchMode === "node-inject") {
7172
- env.NODE_OPTIONS = appendNodeRequire(process.env.NODE_OPTIONS, INJECT_PATH);
7173
- }
7174
- const child = spawn("claude", args, {
7175
- stdio: "inherit",
7176
- env,
7177
- shell: process.platform === "win32"
7178
- });
7179
- const cleanup = /* @__PURE__ */ __name(async () => {
7180
- try {
7181
- server.close();
7182
- } catch {
7183
- }
7184
- await closeSession(void 0, sessionId);
7185
- }, "cleanup");
7186
- process.on("SIGINT", () => child.kill("SIGINT"));
7187
- process.on("SIGTERM", () => child.kill("SIGTERM"));
7188
- return await new Promise((resolve, reject) => {
7189
- child.once("error", async (error) => {
7190
- await cleanup();
7191
- reject(error);
7192
- });
7193
- child.once("exit", async (code, signal) => {
7194
- await cleanup();
7195
- if (signal) process.kill(process.pid, signal);
7196
- resolve(code || 0);
7197
- });
7198
- });
7199
- }
7200
- __name(runClaude, "runClaude");
7201
- module2.exports = runClaude;
7202
- }
7203
- });
7204
-
7205
7066
  // src/commands/claude-proxy.js
7206
7067
  var require_claude_proxy = __commonJS({
7207
7068
  "src/commands/claude-proxy.js"(exports2, module2) {
@@ -7209,6 +7070,7 @@ var require_claude_proxy = __commonJS({
7209
7070
  var fs = require("fs");
7210
7071
  var path = require("path");
7211
7072
  var os = require("os");
7073
+ var http2 = require("http");
7212
7074
  var { spawn, execSync } = require("child_process");
7213
7075
  var chalk2 = require("chalk");
7214
7076
  var {
@@ -7216,12 +7078,20 @@ var require_claude_proxy = __commonJS({
7216
7078
  closeSession,
7217
7079
  getProcessProxyPort,
7218
7080
  getLocalProxyUrl,
7219
- readConfig
7081
+ readConfig,
7082
+ LOCAL_PING_PATH,
7083
+ LOCAL_PING_HEADER,
7084
+ LOCAL_PING_HEADER_VALUE
7220
7085
  } = require_claude_process_proxy();
7221
7086
  var claudeCodeTool = require_claude_code();
7222
7087
  var { getApiKey } = require_config();
7223
- var PID_FILE = path.join(os.homedir(), ".holysheep", "claude-proxy.pid");
7088
+ var HS_DIR = process.env.HOLYSHEEP_DIR || path.join(os.homedir(), ".holysheep");
7089
+ var PID_FILE = path.join(HS_DIR, "claude-proxy.pid");
7090
+ var LOG_FILE = path.join(HS_DIR, "claude-proxy.log");
7224
7091
  var isWin = process.platform === "win32";
7092
+ var DAEMON_READY_TIMEOUT_MS = Number(process.env.HS_CLAUDE_PROXY_DAEMON_READY_TIMEOUT_MS) || 12e3;
7093
+ var DAEMON_READY_INTERVAL_MS = Number(process.env.HS_CLAUDE_PROXY_DAEMON_READY_INTERVAL_MS) || 400;
7094
+ var DAEMON_TEST_CHILD_EXIT = process.env.HS_CLAUDE_PROXY_DAEMON_TEST_CHILD_EXIT === "1";
7225
7095
  function readPid() {
7226
7096
  try {
7227
7097
  const content = fs.readFileSync(PID_FILE, "utf8").trim();
@@ -7237,6 +7107,10 @@ var require_claude_proxy = __commonJS({
7237
7107
  fs.writeFileSync(PID_FILE, JSON.stringify({ pid, port, sessionId, startedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
7238
7108
  }
7239
7109
  __name(writePid, "writePid");
7110
+ function ensureStateDir() {
7111
+ if (!fs.existsSync(HS_DIR)) fs.mkdirSync(HS_DIR, { recursive: true });
7112
+ }
7113
+ __name(ensureStateDir, "ensureStateDir");
7240
7114
  function clearPid() {
7241
7115
  try {
7242
7116
  fs.unlinkSync(PID_FILE);
@@ -7244,6 +7118,19 @@ var require_claude_proxy = __commonJS({
7244
7118
  }
7245
7119
  }
7246
7120
  __name(clearPid, "clearPid");
7121
+ async function readHealthyPid({ pidFile = PID_FILE, timeoutMs = 1e3 } = {}) {
7122
+ let info = null;
7123
+ try {
7124
+ info = JSON.parse(fs.readFileSync(pidFile, "utf8"));
7125
+ } catch {
7126
+ return null;
7127
+ }
7128
+ if (!(info == null ? void 0 : info.pid) || !(info == null ? void 0 : info.port)) return null;
7129
+ if (!isProcessAlive(info.pid)) return null;
7130
+ if (!await isProxyHealthy(info.port, timeoutMs)) return null;
7131
+ return info;
7132
+ }
7133
+ __name(readHealthyPid, "readHealthyPid");
7247
7134
  function isProcessAlive(pid) {
7248
7135
  try {
7249
7136
  process.kill(pid, 0);
@@ -7253,34 +7140,89 @@ var require_claude_proxy = __commonJS({
7253
7140
  }
7254
7141
  }
7255
7142
  __name(isProcessAlive, "isProcessAlive");
7256
- function isProxyHealthy(port) {
7143
+ function isProxyHealthy(port, timeoutMs = 1e3) {
7144
+ return probeLocalProxy(port, timeoutMs);
7145
+ }
7146
+ __name(isProxyHealthy, "isProxyHealthy");
7147
+ function probeLocalProxy(port, timeoutMs = 1e3) {
7148
+ return new Promise((resolve) => {
7149
+ const value = Number(port);
7150
+ if (!Number.isInteger(value) || value <= 0 || value >= 65536) return resolve(false);
7151
+ const req = http2.request({
7152
+ hostname: "127.0.0.1",
7153
+ port: value,
7154
+ path: LOCAL_PING_PATH,
7155
+ method: "GET",
7156
+ timeout: timeoutMs,
7157
+ headers: { "user-agent": "holysheep-claude-proxy-daemon-probe/1" }
7158
+ }, (res) => {
7159
+ const headerOk = String(res.headers[LOCAL_PING_HEADER] || "") === LOCAL_PING_HEADER_VALUE;
7160
+ res.resume();
7161
+ res.on("end", () => resolve(res.statusCode === 200 && headerOk));
7162
+ });
7163
+ req.on("timeout", () => {
7164
+ req.destroy();
7165
+ resolve(false);
7166
+ });
7167
+ req.on("error", () => resolve(false));
7168
+ req.end();
7169
+ });
7170
+ }
7171
+ __name(probeLocalProxy, "probeLocalProxy");
7172
+ function tailLog(lines = 40) {
7257
7173
  try {
7258
- execSync(
7259
- isWin ? `powershell -NonInteractive -Command "if(Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue){exit 0}else{exit 1}"` : `curl -so /dev/null --max-time 1 http://127.0.0.1:${port}/`,
7260
- { stdio: "ignore", timeout: 5e3, windowsHide: true }
7261
- );
7262
- return true;
7174
+ const raw = fs.readFileSync(LOG_FILE, "utf8");
7175
+ return raw.split(/\r?\n/).slice(-lines).join("\n").trim();
7263
7176
  } catch {
7264
- return false;
7177
+ return "";
7265
7178
  }
7266
7179
  }
7267
- __name(isProxyHealthy, "isProxyHealthy");
7180
+ __name(tailLog, "tailLog");
7181
+ function makeDaemonError(message, details = {}) {
7182
+ const logTail = details.logTail !== void 0 ? details.logTail : tailLog();
7183
+ const err = new Error(logTail ? `${message}
7184
+ --- ${LOG_FILE} tail ---
7185
+ ${logTail}` : message);
7186
+ err.code = "CLAUDE_PROXY_DAEMON_FAILED";
7187
+ err.logFile = LOG_FILE;
7188
+ if (details.exitCode !== void 0) err.exitCode = details.exitCode;
7189
+ return err;
7190
+ }
7191
+ __name(makeDaemonError, "makeDaemonError");
7268
7192
  function writeBaseUrlToSettings(port) {
7269
7193
  const settings = claudeCodeTool.readSettings();
7270
7194
  if (!settings.env) settings.env = {};
7271
7195
  settings.env.ANTHROPIC_BASE_URL = getLocalProxyUrl(port);
7196
+ if (settings.env.ANTHROPIC_AUTH_TOKEN === void 0 || settings.env.ANTHROPIC_AUTH_TOKEN === null || settings.env.ANTHROPIC_AUTH_TOKEN === "") {
7197
+ const config = readConfig();
7198
+ const apiKey = config.apiKey || getApiKey();
7199
+ if (apiKey) settings.env.ANTHROPIC_AUTH_TOKEN = apiKey;
7200
+ }
7201
+ settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1";
7202
+ settings.env.HOLYSHEEP_CLAUDE_LAUNCHER = "hs-claude-proxy";
7203
+ settings.env.HOLYSHEEP_CLAUDE_PROCESS_PROXY = "1";
7272
7204
  claudeCodeTool.writeSettings(settings);
7273
7205
  }
7274
7206
  __name(writeBaseUrlToSettings, "writeBaseUrlToSettings");
7275
7207
  function clearBaseUrlFromSettings() {
7276
- var _a, _b;
7208
+ var _a;
7277
7209
  const settings = claudeCodeTool.readSettings();
7278
- if ((_b = (_a = settings.env) == null ? void 0 : _a.ANTHROPIC_BASE_URL) == null ? void 0 : _b.includes("127.0.0.1")) {
7210
+ if (isLocalProxyBaseUrl((_a = settings.env) == null ? void 0 : _a.ANTHROPIC_BASE_URL)) {
7279
7211
  delete settings.env.ANTHROPIC_BASE_URL;
7280
7212
  claudeCodeTool.writeSettings(settings);
7281
7213
  }
7282
7214
  }
7283
7215
  __name(clearBaseUrlFromSettings, "clearBaseUrlFromSettings");
7216
+ function isLocalProxyBaseUrl(value) {
7217
+ if (!value) return false;
7218
+ try {
7219
+ const url = new URL(String(value));
7220
+ return (url.hostname === "127.0.0.1" || url.hostname === "localhost") && (url.protocol === "http:" || url.protocol === "https:");
7221
+ } catch {
7222
+ return String(value).includes("127.0.0.1") || String(value).includes("localhost");
7223
+ }
7224
+ }
7225
+ __name(isLocalProxyBaseUrl, "isLocalProxyBaseUrl");
7284
7226
  async function handleStop() {
7285
7227
  const info = readPid();
7286
7228
  if (!info) {
@@ -7307,16 +7249,16 @@ var require_claude_proxy = __commonJS({
7307
7249
  clearPid();
7308
7250
  }
7309
7251
  __name(handleStop, "handleStop");
7310
- function handleStatus() {
7252
+ async function handleStatus() {
7311
7253
  const info = readPid();
7312
7254
  if (!info) {
7313
7255
  console.log(chalk2.yellow("\u4EE3\u7406\u672A\u542F\u52A8"));
7314
7256
  return;
7315
7257
  }
7316
7258
  const alive = isProcessAlive(info.pid);
7317
- const healthy = alive && isProxyHealthy(info.port);
7318
- if (healthy) {
7319
- console.log(chalk2.green(`\u4EE3\u7406\u8FD0\u884C\u4E2D`));
7259
+ const healthy = alive && await isProxyHealthy(info.port);
7260
+ if (alive && healthy) {
7261
+ console.log(chalk2.green(`\u4EE3\u7406\u5DF2\u8FD0\u884C`));
7320
7262
  console.log(chalk2.gray(` PID: ${info.pid}`));
7321
7263
  console.log(chalk2.gray(` \u7AEF\u53E3: ${info.port}`));
7322
7264
  console.log(chalk2.gray(` \u5730\u5740: ${getLocalProxyUrl(info.port)}`));
@@ -7331,37 +7273,71 @@ var require_claude_proxy = __commonJS({
7331
7273
  }
7332
7274
  __name(handleStatus, "handleStatus");
7333
7275
  async function handleDaemon() {
7276
+ if (process.env.HS_CLAUDE_PROXY_DAEMON_TEST_SPAWN_FAIL === "1") {
7277
+ throw makeDaemonError("\u4EE3\u7406\u5B50\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: \u6545\u969C\u6CE8\u5165 HS_CLAUDE_PROXY_DAEMON_TEST_SPAWN_FAIL");
7278
+ }
7334
7279
  const info = readPid();
7335
- if (info && isProcessAlive(info.pid) && isProxyHealthy(info.port)) {
7280
+ if (info && isProcessAlive(info.pid) && await isProxyHealthy(info.port)) {
7281
+ writeBaseUrlToSettings(info.port);
7336
7282
  console.log(chalk2.green(`\u4EE3\u7406\u5DF2\u5728\u8FD0\u884C (PID ${info.pid}, \u7AEF\u53E3 ${info.port})`));
7337
7283
  return;
7338
7284
  }
7339
7285
  const { selfEntrypoint } = require_paths();
7340
7286
  const scriptPath = selfEntrypoint();
7341
- const spawnCmd = isWin ? "node" : process.execPath;
7287
+ const spawnCmd = process.execPath;
7288
+ ensureStateDir();
7289
+ fs.appendFileSync(LOG_FILE, `
7290
+ [${(/* @__PURE__ */ new Date()).toISOString()}] starting daemon via ${spawnCmd} ${scriptPath} claude-proxy
7291
+ `, "utf8");
7292
+ const outFd = fs.openSync(LOG_FILE, "a");
7293
+ const errFd = fs.openSync(LOG_FILE, "a");
7342
7294
  const child = spawn(spawnCmd, [scriptPath, "claude-proxy"], {
7343
7295
  detached: true,
7344
- stdio: "ignore",
7345
- windowsHide: true
7296
+ stdio: ["ignore", outFd, errFd],
7297
+ windowsHide: true,
7298
+ env: { ...process.env, HS_CLAUDE_PROXY_DAEMON_CHILD: "1" }
7299
+ });
7300
+ child.on("error", (err) => {
7301
+ try {
7302
+ fs.appendFileSync(LOG_FILE, `[${(/* @__PURE__ */ new Date()).toISOString()}] spawn error: ${err.message}
7303
+ `, "utf8");
7304
+ } catch {
7305
+ }
7346
7306
  });
7307
+ fs.closeSync(outFd);
7308
+ fs.closeSync(errFd);
7347
7309
  child.unref();
7348
- const port = getProcessProxyPort();
7349
- for (let i = 0; i < 15; i++) {
7350
- await new Promise((r) => setTimeout(r, 500));
7351
- if (isProxyHealthy(port)) {
7310
+ const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
7311
+ while (Date.now() < deadline) {
7312
+ await new Promise((r) => setTimeout(r, DAEMON_READY_INTERVAL_MS));
7313
+ const rawLatest = readPid();
7314
+ if (rawLatest && rawLatest.pid && !isProcessAlive(rawLatest.pid)) {
7315
+ clearPid();
7316
+ throw makeDaemonError(`\u4EE3\u7406\u5B50\u8FDB\u7A0B\u5DF2\u9000\u51FA (PID ${rawLatest.pid})`);
7317
+ }
7318
+ const latest = await readHealthyPid({ timeoutMs: 1e3 });
7319
+ if (latest) {
7320
+ writeBaseUrlToSettings(latest.port);
7352
7321
  console.log(chalk2.green(`\u4EE3\u7406\u5DF2\u5728\u540E\u53F0\u542F\u52A8`));
7353
- console.log(chalk2.gray(` PID: ${child.pid}`));
7354
- console.log(chalk2.gray(` \u7AEF\u53E3: ${port}`));
7355
- console.log(chalk2.gray(` \u5730\u5740: ${getLocalProxyUrl(port)}`));
7322
+ console.log(chalk2.gray(` PID: ${latest.pid}`));
7323
+ console.log(chalk2.gray(` \u7AEF\u53E3: ${latest.port}`));
7324
+ console.log(chalk2.gray(` \u5730\u5740: ${getLocalProxyUrl(latest.port)}`));
7325
+ console.log(chalk2.gray(` \u65E5\u5FD7: ${LOG_FILE}`));
7356
7326
  console.log(chalk2.cyan("\n VS Code Claude \u6269\u5C55\u73B0\u5728\u53EF\u4EE5\u4F7F\u7528\u4E86"));
7357
7327
  return;
7358
7328
  }
7329
+ if (child.exitCode !== null) {
7330
+ throw makeDaemonError(`\u4EE3\u7406\u5B50\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25 (exit=${child.exitCode})`, { exitCode: child.exitCode });
7331
+ }
7359
7332
  }
7360
- console.log(chalk2.yellow("\u4EE3\u7406\u542F\u52A8\u4E2D\uFF0C\u8BF7\u7A0D\u7B49..."));
7361
- console.log(chalk2.gray(` PID: ${child.pid}`));
7333
+ throw makeDaemonError(`\u4EE3\u7406\u542F\u52A8\u8D85\u65F6\uFF0C\u672A\u901A\u8FC7\u672C\u5730 HTTP \u6307\u7EB9\u5065\u5EB7\u68C0\u67E5 (log: ${LOG_FILE})`);
7362
7334
  }
7363
7335
  __name(handleDaemon, "handleDaemon");
7364
7336
  async function handleForeground() {
7337
+ if (DAEMON_TEST_CHILD_EXIT) {
7338
+ console.error("[hs-claude-proxy] daemon test child forced exit");
7339
+ process.exit(42);
7340
+ }
7365
7341
  const config = readConfig();
7366
7342
  const apiKey = config.apiKey || getApiKey();
7367
7343
  if (!apiKey) {
@@ -7369,14 +7345,12 @@ var require_claude_proxy = __commonJS({
7369
7345
  process.exit(1);
7370
7346
  }
7371
7347
  const existing = readPid();
7372
- if (existing && isProcessAlive(existing.pid) && isProxyHealthy(existing.port)) {
7348
+ if (existing && isProcessAlive(existing.pid) && await isProxyHealthy(existing.port)) {
7373
7349
  console.log(chalk2.yellow(`\u4EE3\u7406\u5DF2\u5728\u8FD0\u884C (PID ${existing.pid}, \u7AEF\u53E3 ${existing.port})\uFF0C\u5148\u505C\u6B62: hs claude-proxy --stop`));
7374
7350
  process.exit(1);
7375
7351
  }
7376
- const ensureClaudeProxyConfig = require_claude().ensureClaudeProxyConfig || (() => {
7377
- });
7378
7352
  try {
7379
- ensureClaudeProxyConfig(apiKey);
7353
+ claudeProxy.ensureClaudeProxyConfig(apiKey);
7380
7354
  } catch {
7381
7355
  }
7382
7356
  console.log(chalk2.gray("\u542F\u52A8 Claude \u4EE3\u7406..."));
@@ -7419,11 +7393,17 @@ var require_claude_proxy = __commonJS({
7419
7393
  const claudeCodeTool2 = require_claude_code();
7420
7394
  const proxy = require_claude_process_proxy();
7421
7395
  const config = proxy.readConfig();
7422
- if (!config.apiKey || !config.bridgeSecret) {
7423
- const bridgeConfig = claudeCodeTool2.buildBridgeConfig(apiKey, void 0, config);
7396
+ const bridgeConfig = claudeCodeTool2.buildBridgeConfig(apiKey, void 0, config);
7397
+ if (JSON.stringify(bridgeConfig) !== JSON.stringify(config)) {
7424
7398
  proxy.writeConfig(bridgeConfig);
7425
7399
  }
7426
7400
  };
7401
+ claudeProxy._private = {
7402
+ probeLocalProxy,
7403
+ readHealthyPid,
7404
+ daemonLogFile: /* @__PURE__ */ __name(() => LOG_FILE, "daemonLogFile"),
7405
+ daemonPidFile: /* @__PURE__ */ __name(() => PID_FILE, "daemonPidFile")
7406
+ };
7427
7407
  module2.exports = claudeProxy;
7428
7408
  }
7429
7409
  });
@@ -10916,6 +10896,145 @@ var require_webui = __commonJS({
10916
10896
  }
10917
10897
  });
10918
10898
 
10899
+ // src/commands/claude.js
10900
+ var require_claude = __commonJS({
10901
+ "src/commands/claude.js"(exports2, module2) {
10902
+ "use strict";
10903
+ var path = require("path");
10904
+ var { spawn } = require("child_process");
10905
+ var {
10906
+ BASE_URL_ANTHROPIC,
10907
+ BASE_URL_CLAUDE_RELAY,
10908
+ getApiKey
10909
+ } = require_config();
10910
+ var {
10911
+ closeSession,
10912
+ getLocalProxyUrl,
10913
+ startProcessProxy,
10914
+ readConfig,
10915
+ writeConfig
10916
+ } = require_claude_process_proxy();
10917
+ var claudeCodeTool = require_claude_code();
10918
+ var { processProxyInjectPath } = require_paths();
10919
+ var INJECT_PATH = processProxyInjectPath();
10920
+ function appendNodeRequire(existingValue, requirePath) {
10921
+ const nextFlag = `--require ${requirePath}`;
10922
+ if (!existingValue) return nextFlag;
10923
+ return existingValue.includes(nextFlag) ? existingValue : `${existingValue} ${nextFlag}`.trim();
10924
+ }
10925
+ __name(appendNodeRequire, "appendNodeRequire");
10926
+ function mergeNoProxy(existingValue, extraHosts = []) {
10927
+ const merged = /* @__PURE__ */ new Set();
10928
+ for (const chunk of String(existingValue || "").split(",")) {
10929
+ const value = chunk.trim();
10930
+ if (value) merged.add(value);
10931
+ }
10932
+ for (const host of extraHosts) {
10933
+ if (host) merged.add(host);
10934
+ }
10935
+ return Array.from(merged).join(",");
10936
+ }
10937
+ __name(mergeNoProxy, "mergeNoProxy");
10938
+ function ensureClaudeProxyConfig(apiKey) {
10939
+ const config = readConfig();
10940
+ const next = claudeCodeTool.buildBridgeConfig(apiKey, BASE_URL_ANTHROPIC, {
10941
+ ...config,
10942
+ relayUrl: config.relayUrl || BASE_URL_CLAUDE_RELAY
10943
+ });
10944
+ const changed = JSON.stringify(next) !== JSON.stringify(config);
10945
+ if (changed) {
10946
+ writeConfig(next);
10947
+ return next;
10948
+ }
10949
+ return config;
10950
+ }
10951
+ __name(ensureClaudeProxyConfig, "ensureClaudeProxyConfig");
10952
+ async function runClaude(args = []) {
10953
+ var _a;
10954
+ const config = readConfig();
10955
+ const apiKey = config.apiKey || getApiKey();
10956
+ if (!apiKey) {
10957
+ throw new Error("Missing API Key. Run hs setup first.");
10958
+ }
10959
+ try {
10960
+ const { scheduleUpgradeBannerCheck } = require_version_check();
10961
+ const pkg2 = require_package();
10962
+ scheduleUpgradeBannerCheck({ localVersion: pkg2.version });
10963
+ } catch {
10964
+ }
10965
+ ensureClaudeProxyConfig(apiKey);
10966
+ const runtime = typeof claudeCodeTool.detectClaudeRuntime === "function" ? claudeCodeTool.detectClaudeRuntime() : { kind: "unknown", launchMode: "env-proxy" };
10967
+ if (runtime.kind === "missing") {
10968
+ const installCmd = process.platform === "win32" ? "irm https://claude.ai/install.ps1 | iex" : "curl -fsSL https://claude.ai/install.sh | bash";
10969
+ const err = new Error(
10970
+ `\u672A\u627E\u5230 claude \u547D\u4EE4 \u2014 Claude Code \u5C1A\u672A\u5B89\u88C5\u3002
10971
+ \u8BF7\u5148\u8FD0\u884C: ${installCmd}
10972
+ \u5B89\u88C5\u540E\u91CD\u5F00\u7EC8\u7AEF\u518D\u6267\u884C hs claude\u3002`
10973
+ );
10974
+ err.code = "CLAUDE_NOT_INSTALLED";
10975
+ err.exitCode = 127;
10976
+ throw err;
10977
+ }
10978
+ const settings = claudeCodeTool.readSettings();
10979
+ if ((_a = settings.env) == null ? void 0 : _a.ANTHROPIC_BASE_URL) {
10980
+ delete settings.env.ANTHROPIC_BASE_URL;
10981
+ claudeCodeTool.writeSettings(settings);
10982
+ }
10983
+ const { server, port, sessionId } = await startProcessProxy({});
10984
+ const proxyUrl = getLocalProxyUrl(port);
10985
+ const launchMode = runtime.launchMode === "node-inject" ? "local-api + connect-fallback + node-inject" : "whole-process-proxy + local-api";
10986
+ const env = {
10987
+ ...process.env,
10988
+ ANTHROPIC_API_KEY: void 0,
10989
+ ANTHROPIC_AUTH_TOKEN: apiKey,
10990
+ ANTHROPIC_BASE_URL: proxyUrl,
10991
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
10992
+ HOLYSHEEP_CLAUDE_PROCESS_PROXY: "1",
10993
+ HOLYSHEEP_CLAUDE_SESSION_ID: sessionId,
10994
+ HS_PROXY_URL: proxyUrl,
10995
+ HOLYSHEEP_CLAUDE_RUNTIME_KIND: runtime.kind || "unknown",
10996
+ HOLYSHEEP_CLAUDE_LAUNCH_MODE: launchMode,
10997
+ HTTP_PROXY: proxyUrl,
10998
+ HTTPS_PROXY: proxyUrl,
10999
+ ALL_PROXY: proxyUrl,
11000
+ NO_PROXY: mergeNoProxy(process.env.NO_PROXY, ["127.0.0.1", "localhost"]),
11001
+ ANTHROPIC_MAX_RETRIES: "0",
11002
+ MAX_RETRIES: "0"
11003
+ };
11004
+ if (runtime.launchMode === "node-inject") {
11005
+ env.NODE_OPTIONS = appendNodeRequire(process.env.NODE_OPTIONS, INJECT_PATH);
11006
+ }
11007
+ const child = spawn("claude", args, {
11008
+ stdio: "inherit",
11009
+ env,
11010
+ shell: process.platform === "win32"
11011
+ });
11012
+ const cleanup = /* @__PURE__ */ __name(async () => {
11013
+ try {
11014
+ server.close();
11015
+ } catch {
11016
+ }
11017
+ await closeSession(void 0, sessionId);
11018
+ }, "cleanup");
11019
+ process.on("SIGINT", () => child.kill("SIGINT"));
11020
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
11021
+ return await new Promise((resolve, reject) => {
11022
+ child.once("error", async (error) => {
11023
+ await cleanup();
11024
+ reject(error);
11025
+ });
11026
+ child.once("exit", async (code, signal) => {
11027
+ await cleanup();
11028
+ if (signal) process.kill(process.pid, signal);
11029
+ resolve(code || 0);
11030
+ });
11031
+ });
11032
+ }
11033
+ __name(runClaude, "runClaude");
11034
+ module2.exports = runClaude;
11035
+ }
11036
+ });
11037
+
10919
11038
  // src/index.js
10920
11039
  var { program } = require("commander");
10921
11040
  var chalk = require("chalk");
@@ -11041,7 +11160,18 @@ ${err.message}
11041
11160
  });
11042
11161
  program.command("claude-proxy [args...]").allowUnknownOption(true).description("\u542F\u52A8 Claude \u4EE3\u7406\u670D\u52A1\uFF08\u8BA9 VS Code Claude \u6269\u5C55\u4E5F\u80FD\u7528 HolySheep\uFF09").action(async (args = []) => {
11043
11162
  const claudeProxy = require_claude_proxy();
11044
- await claudeProxy(args);
11163
+ try {
11164
+ await claudeProxy(args);
11165
+ } catch (err) {
11166
+ if (err && err.code === "CLAUDE_PROXY_DAEMON_FAILED") {
11167
+ process.stderr.write(`
11168
+ ${err.message}
11169
+
11170
+ `);
11171
+ process.exit(typeof err.exitCode === "number" ? err.exitCode : 1);
11172
+ }
11173
+ throw err;
11174
+ }
11045
11175
  });
11046
11176
  program.parse(process.argv);
11047
11177
  if (process.argv.slice(2).length === 0) {
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@simonyea/holysheep-cli",
3
- "version": "2.1.64",
3
+ "version": "2.1.66",
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/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.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/acptypes-patch.test.js && node tests/codex-approval-policy.test.js && node tests/version-check.test.js && node tests/runclaude-missing-binary.test.js",
8
8
  "prepublishOnly": "npm run build && npm test && node scripts/check-tarball-size.js"
9
9
  },
10
10
  "keywords": [