@simonyea/holysheep-cli 2.1.42 → 2.1.44

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.
@@ -76,21 +76,25 @@ var require_claude_process_proxy = __commonJS({
76
76
  var 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
+ var LOCAL_PING_PATH = "/__hs_local_ping__";
80
+ var LOCAL_PING_HEADER = "x-hs-local-proxy";
81
+ var LOCAL_PING_HEADER_VALUE = "1";
79
82
  function ensureDir() {
80
83
  if (!fs.existsSync(HOLYSHEEP_DIR)) fs.mkdirSync(HOLYSHEEP_DIR, { recursive: true });
81
84
  }
82
85
  __name(ensureDir, "ensureDir");
83
- function readConfig() {
86
+ function readConfig(configPath) {
84
87
  try {
85
- return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
88
+ return JSON.parse(fs.readFileSync(configPath || CONFIG_PATH, "utf8"));
86
89
  } catch {
87
90
  return {};
88
91
  }
89
92
  }
90
93
  __name(readConfig, "readConfig");
91
- function writeConfig(data) {
92
- ensureDir();
93
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2), "utf8");
94
+ function writeConfig(data, configPath) {
95
+ const target = configPath || CONFIG_PATH;
96
+ if (!configPath) ensureDir();
97
+ fs.writeFileSync(target, JSON.stringify(data, null, 2), "utf8");
94
98
  }
95
99
  __name(writeConfig, "writeConfig");
96
100
  function getProcessProxyPort(config = readConfig()) {
@@ -572,6 +576,31 @@ var require_claude_process_proxy = __commonJS({
572
576
  __name(pipeWithCleanup, "pipeWithCleanup");
573
577
  function createProcessProxyServer({ sessionId, configPath = CONFIG_PATH, allowAnthropicConnect = false }) {
574
578
  const server = http.createServer(async (clientReq, clientRes) => {
579
+ if (clientReq.method === "GET" && !clientReq.url.startsWith("http")) {
580
+ const u = clientReq.url.split("?")[0];
581
+ if (u === LOCAL_PING_PATH) {
582
+ const hostHeader = String(clientReq.headers.host || "").toLowerCase();
583
+ const isLoopbackHost = hostHeader.startsWith("127.0.0.1") || hostHeader.startsWith("localhost") || hostHeader.startsWith("[::1]");
584
+ if (!isLoopbackHost) {
585
+ clientRes.writeHead(404, { "content-type": "text/plain" });
586
+ clientRes.end("not found");
587
+ return;
588
+ }
589
+ const body = JSON.stringify({
590
+ hsLocalProxy: true,
591
+ sessionId: String(sessionId || ""),
592
+ pid: process.pid,
593
+ ts: Date.now()
594
+ });
595
+ clientRes.writeHead(200, {
596
+ "content-type": "application/json",
597
+ [LOCAL_PING_HEADER]: LOCAL_PING_HEADER_VALUE,
598
+ "cache-control": "no-store"
599
+ });
600
+ clientRes.end(body);
601
+ return;
602
+ }
603
+ }
575
604
  const isDirect = !clientReq.url.startsWith("http");
576
605
  if (ENABLE_TIMING_LOG) {
577
606
  console.error(
@@ -885,7 +914,16 @@ ${body}`);
885
914
  }
886
915
  });
887
916
  server.once("listening", () => {
888
- resolve({ server, port: server.address().port, sessionId: effectiveSessionId });
917
+ const boundPort = server.address().port;
918
+ try {
919
+ const latest = readConfig(configPath);
920
+ if (Number(latest.processProxyPort) !== boundPort) {
921
+ writeConfig({ ...latest, processProxyPort: boundPort }, configPath);
922
+ }
923
+ } catch (persistErr) {
924
+ console.error("[hs-claude-proxy] warn: failed to persist processProxyPort:", persistErr && persistErr.message);
925
+ }
926
+ resolve({ server, port: boundPort, sessionId: effectiveSessionId });
889
927
  });
890
928
  server.listen(p, "127.0.0.1");
891
929
  }, "tryListen");
@@ -931,7 +969,10 @@ ${body}`);
931
969
  getControlPlaneUrl,
932
970
  readConfig,
933
971
  startProcessProxy,
934
- writeConfig
972
+ writeConfig,
973
+ LOCAL_PING_PATH,
974
+ LOCAL_PING_HEADER,
975
+ LOCAL_PING_HEADER_VALUE
935
976
  };
936
977
  }
937
978
  });
@@ -3931,11 +3972,11 @@ var require_package = __commonJS({
3931
3972
  "package.json"(exports2, module2) {
3932
3973
  module2.exports = {
3933
3974
  name: "@simonyea/holysheep-cli",
3934
- version: "2.1.42",
3975
+ version: "2.1.44",
3935
3976
  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.",
3936
3977
  scripts: {
3937
3978
  build: "node scripts/build.mjs",
3938
- 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/opencode-default-model.test.js && node tests/paths-bundled.test.js",
3979
+ 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/opencode-default-model.test.js && node tests/paths-bundled.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",
3939
3980
  prepublishOnly: "npm run build && npm test && node scripts/check-tarball-size.js"
3940
3981
  },
3941
3982
  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.42",
15
+ version: "2.1.44",
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/opencode-default-model.test.js && node tests/paths-bundled.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/opencode-default-model.test.js && node tests/paths-bundled.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",
20
20
  prepublishOnly: "npm run build && npm test && node scripts/check-tarball-size.js"
21
21
  },
22
22
  keywords: [
@@ -752,21 +752,25 @@ var require_claude_process_proxy = __commonJS({
752
752
  var HOLYSHEEP_DIR = path.join(os.homedir(), ".holysheep");
753
753
  var CONFIG_PATH = path.join(HOLYSHEEP_DIR, "claude-proxy.json");
754
754
  var DEFAULT_PROXY_PORT = 14556;
755
+ var LOCAL_PING_PATH = "/__hs_local_ping__";
756
+ var LOCAL_PING_HEADER = "x-hs-local-proxy";
757
+ var LOCAL_PING_HEADER_VALUE = "1";
755
758
  function ensureDir() {
756
759
  if (!fs.existsSync(HOLYSHEEP_DIR)) fs.mkdirSync(HOLYSHEEP_DIR, { recursive: true });
757
760
  }
758
761
  __name(ensureDir, "ensureDir");
759
- function readConfig() {
762
+ function readConfig(configPath) {
760
763
  try {
761
- return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
764
+ return JSON.parse(fs.readFileSync(configPath || CONFIG_PATH, "utf8"));
762
765
  } catch {
763
766
  return {};
764
767
  }
765
768
  }
766
769
  __name(readConfig, "readConfig");
767
- function writeConfig(data) {
768
- ensureDir();
769
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2), "utf8");
770
+ function writeConfig(data, configPath) {
771
+ const target = configPath || CONFIG_PATH;
772
+ if (!configPath) ensureDir();
773
+ fs.writeFileSync(target, JSON.stringify(data, null, 2), "utf8");
770
774
  }
771
775
  __name(writeConfig, "writeConfig");
772
776
  function getProcessProxyPort(config = readConfig()) {
@@ -1248,6 +1252,31 @@ var require_claude_process_proxy = __commonJS({
1248
1252
  __name(pipeWithCleanup, "pipeWithCleanup");
1249
1253
  function createProcessProxyServer({ sessionId, configPath = CONFIG_PATH, allowAnthropicConnect = false }) {
1250
1254
  const server = http.createServer(async (clientReq, clientRes) => {
1255
+ if (clientReq.method === "GET" && !clientReq.url.startsWith("http")) {
1256
+ const u = clientReq.url.split("?")[0];
1257
+ if (u === LOCAL_PING_PATH) {
1258
+ const hostHeader = String(clientReq.headers.host || "").toLowerCase();
1259
+ const isLoopbackHost = hostHeader.startsWith("127.0.0.1") || hostHeader.startsWith("localhost") || hostHeader.startsWith("[::1]");
1260
+ if (!isLoopbackHost) {
1261
+ clientRes.writeHead(404, { "content-type": "text/plain" });
1262
+ clientRes.end("not found");
1263
+ return;
1264
+ }
1265
+ const body = JSON.stringify({
1266
+ hsLocalProxy: true,
1267
+ sessionId: String(sessionId || ""),
1268
+ pid: process.pid,
1269
+ ts: Date.now()
1270
+ });
1271
+ clientRes.writeHead(200, {
1272
+ "content-type": "application/json",
1273
+ [LOCAL_PING_HEADER]: LOCAL_PING_HEADER_VALUE,
1274
+ "cache-control": "no-store"
1275
+ });
1276
+ clientRes.end(body);
1277
+ return;
1278
+ }
1279
+ }
1251
1280
  const isDirect = !clientReq.url.startsWith("http");
1252
1281
  if (ENABLE_TIMING_LOG) {
1253
1282
  console.error(
@@ -1561,7 +1590,16 @@ ${body}`);
1561
1590
  }
1562
1591
  });
1563
1592
  server.once("listening", () => {
1564
- resolve({ server, port: server.address().port, sessionId: effectiveSessionId });
1593
+ const boundPort = server.address().port;
1594
+ try {
1595
+ const latest = readConfig(configPath);
1596
+ if (Number(latest.processProxyPort) !== boundPort) {
1597
+ writeConfig({ ...latest, processProxyPort: boundPort }, configPath);
1598
+ }
1599
+ } catch (persistErr) {
1600
+ console.error("[hs-claude-proxy] warn: failed to persist processProxyPort:", persistErr && persistErr.message);
1601
+ }
1602
+ resolve({ server, port: boundPort, sessionId: effectiveSessionId });
1565
1603
  });
1566
1604
  server.listen(p, "127.0.0.1");
1567
1605
  }, "tryListen");
@@ -1607,7 +1645,10 @@ ${body}`);
1607
1645
  getControlPlaneUrl,
1608
1646
  readConfig,
1609
1647
  startProcessProxy,
1610
- writeConfig
1648
+ writeConfig,
1649
+ LOCAL_PING_PATH,
1650
+ LOCAL_PING_HEADER,
1651
+ LOCAL_PING_HEADER_VALUE
1611
1652
  };
1612
1653
  }
1613
1654
  });
@@ -8530,8 +8571,12 @@ var require_aionui_wrapper = __commonJS({
8530
8571
  getApiKey,
8531
8572
  loadConfig,
8532
8573
  saveConfig,
8533
- BASE_URL_OPENAI
8574
+ BASE_URL_OPENAI,
8575
+ BASE_URL_ANTHROPIC,
8576
+ BASE_URL_CLAUDE_RELAY
8534
8577
  } = require_config();
8578
+ var claudeProcessProxy = require_claude_process_proxy();
8579
+ var claudeCodeTool = require_claude_code();
8535
8580
  var BRIDGE_DIR = path.join(os.homedir(), ".holysheep");
8536
8581
  var BRIDGE_CRED_FILE = path.join(BRIDGE_DIR, "aionui-bridge.json");
8537
8582
  var TOKEN_TTL_MS = 3e4;
@@ -8984,6 +9029,117 @@ var require_aionui_wrapper = __commonJS({
8984
9029
  }, "onRequest");
8985
9030
  }
8986
9031
  __name(buildRouter, "buildRouter");
9032
+ function probeLocalPort(port, timeoutMs = 800) {
9033
+ return new Promise((resolve) => {
9034
+ const socket = net.createConnection({ host: "127.0.0.1", port });
9035
+ let tcpSettled = false;
9036
+ const tcpTimer = setTimeout(() => {
9037
+ if (tcpSettled) return;
9038
+ tcpSettled = true;
9039
+ try {
9040
+ socket.destroy();
9041
+ } catch {
9042
+ }
9043
+ resolve(false);
9044
+ }, timeoutMs);
9045
+ socket.once("connect", () => {
9046
+ if (tcpSettled) return;
9047
+ tcpSettled = true;
9048
+ clearTimeout(tcpTimer);
9049
+ try {
9050
+ socket.end();
9051
+ } catch {
9052
+ }
9053
+ const req = http.request({
9054
+ host: "127.0.0.1",
9055
+ port,
9056
+ method: "GET",
9057
+ path: "/__hs_local_ping__",
9058
+ timeout: timeoutMs,
9059
+ headers: { "host": `127.0.0.1:${port}`, "connection": "close" }
9060
+ }, (res) => {
9061
+ const chunks = [];
9062
+ res.on("data", (c) => chunks.push(c));
9063
+ res.on("end", () => {
9064
+ const header = String(res.headers["x-hs-local-proxy"] || "");
9065
+ if (res.statusCode !== 200 || header !== "1") {
9066
+ resolve(false);
9067
+ return;
9068
+ }
9069
+ try {
9070
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
9071
+ resolve(parsed && parsed.hsLocalProxy === true);
9072
+ } catch {
9073
+ resolve(false);
9074
+ }
9075
+ });
9076
+ res.on("error", () => resolve(false));
9077
+ });
9078
+ req.on("timeout", () => {
9079
+ try {
9080
+ req.destroy();
9081
+ } catch {
9082
+ }
9083
+ ;
9084
+ resolve(false);
9085
+ });
9086
+ req.on("error", () => resolve(false));
9087
+ req.end();
9088
+ });
9089
+ socket.once("error", () => {
9090
+ if (tcpSettled) return;
9091
+ tcpSettled = true;
9092
+ clearTimeout(tcpTimer);
9093
+ resolve(false);
9094
+ });
9095
+ });
9096
+ }
9097
+ __name(probeLocalPort, "probeLocalPort");
9098
+ function _ensureClaudeProxyConfig(apiKey) {
9099
+ const config = claudeProcessProxy.readConfig();
9100
+ const next = claudeCodeTool.buildBridgeConfig(apiKey, BASE_URL_ANTHROPIC, {
9101
+ ...config,
9102
+ relayUrl: config.relayUrl || BASE_URL_CLAUDE_RELAY
9103
+ });
9104
+ if (JSON.stringify(next) !== JSON.stringify(config)) {
9105
+ claudeProcessProxy.writeConfig(next);
9106
+ return next;
9107
+ }
9108
+ return config;
9109
+ }
9110
+ __name(_ensureClaudeProxyConfig, "_ensureClaudeProxyConfig");
9111
+ async function ensureClaudeProcessProxyRunning(opts = {}) {
9112
+ const proxyModule = opts.proxyModule || claudeProcessProxy;
9113
+ const probe = opts.probe || probeLocalPort;
9114
+ const apiKeyGetter = opts.apiKeyGetter || getApiKey;
9115
+ const ensureConfig = opts.ensureConfig || _ensureClaudeProxyConfig;
9116
+ const port = proxyModule.getProcessProxyPort(proxyModule.readConfig());
9117
+ const listening = await probe(port, 500);
9118
+ if (listening) {
9119
+ log(`claude process proxy already listening on 127.0.0.1:${port} (reusing; likely started by \`hs claude\` or another hs web)`);
9120
+ return { reused: true, port };
9121
+ }
9122
+ let apiKey;
9123
+ try {
9124
+ apiKey = apiKeyGetter();
9125
+ } catch {
9126
+ apiKey = null;
9127
+ }
9128
+ if (!apiKey) {
9129
+ log(`claude process proxy pre-start skipped: no HolySheep API key found (run \`hs login\` to enable Claude Code). Non-Claude backends unaffected.`);
9130
+ return { skipped: true, reason: "no-api-key", port };
9131
+ }
9132
+ try {
9133
+ ensureConfig(apiKey);
9134
+ const result = await proxyModule.startProcessProxy({});
9135
+ log(`claude process proxy listening on 127.0.0.1:${result.port} (sessionId=${String(result.sessionId).slice(0, 8)})`);
9136
+ return { reused: false, port: result.port, sessionId: result.sessionId, server: result.server };
9137
+ } catch (err) {
9138
+ log(`claude process proxy pre-start failed (continuing without it): ${err && err.message ? err.message : err}`);
9139
+ return { skipped: true, reason: "start-failed", port, error: err };
9140
+ }
9141
+ }
9142
+ __name(ensureClaudeProcessProxyRunning, "ensureClaudeProcessProxyRunning");
8987
9143
  async function startWrapper({ port, runtimeDir, runtimeVersion, runtimeSource, bunPath }) {
8988
9144
  const hsNative = detectHolySheepAionUi(runtimeDir);
8989
9145
  log(`/login mode: ${hsNative ? "holysheep-native (apiKey)" : "legacy (username/password bridge)"}`);
@@ -8992,6 +9148,7 @@ var require_aionui_wrapper = __commonJS({
8992
9148
  }
8993
9149
  const internalPort = await pickInternalPort();
8994
9150
  log(`internal runtime port: ${internalPort}`);
9151
+ const claudeProxyHandle = await ensureClaudeProcessProxyRunning();
8995
9152
  const debug = process.env.HS_WEB_DEBUG === "1";
8996
9153
  const { PACKAGE_ROOT, cliVersion, ptyHermesWrapperPath } = require_paths();
8997
9154
  const cliRoot = PACKAGE_ROOT;
@@ -9035,10 +9192,33 @@ var require_aionui_wrapper = __commonJS({
9035
9192
  }, "appendLog");
9036
9193
  aionui.stdout.on("data", (d) => appendLog("out", d));
9037
9194
  aionui.stderr.on("data", (d) => appendLog("err", d));
9195
+ let claudeProxyCleanedUp = false;
9196
+ const cleanupClaudeProxy = /* @__PURE__ */ __name(() => {
9197
+ if (claudeProxyCleanedUp) return;
9198
+ claudeProxyCleanedUp = true;
9199
+ if (!claudeProxyHandle || claudeProxyHandle.skipped || claudeProxyHandle.reused) return;
9200
+ try {
9201
+ if (claudeProxyHandle.server && typeof claudeProxyHandle.server.close === "function") {
9202
+ claudeProxyHandle.server.close();
9203
+ }
9204
+ } catch {
9205
+ }
9206
+ if (claudeProxyHandle.sessionId) {
9207
+ Promise.resolve(claudeProcessProxy.closeSession(void 0, claudeProxyHandle.sessionId)).catch(() => {
9208
+ });
9209
+ }
9210
+ }, "cleanupClaudeProxy");
9038
9211
  aionui.on("exit", (code) => {
9039
9212
  log(`runtime upstream exited (code=${code})`);
9213
+ cleanupClaudeProxy();
9040
9214
  process.exit(code || 1);
9041
9215
  });
9216
+ const _onSignal = /* @__PURE__ */ __name(() => {
9217
+ cleanupClaudeProxy();
9218
+ }, "_onSignal");
9219
+ process.once("SIGINT", _onSignal);
9220
+ process.once("SIGTERM", _onSignal);
9221
+ process.once("exit", cleanupClaudeProxy);
9042
9222
  try {
9043
9223
  await waitForUpstreamReady(internalPort);
9044
9224
  } catch (e) {
@@ -9089,7 +9269,10 @@ ${tail.split(/\r?\n/).map((ln) => ` ${ln}`).join("\n")}
9089
9269
  pruneExpiredTokens,
9090
9270
  _tokens: bootstrapTokens,
9091
9271
  TOKEN_TTL_MS,
9092
- BRIDGE_CRED_FILE
9272
+ BRIDGE_CRED_FILE,
9273
+ // [HolySheep v2.1.43] Exported for aionui-wrapper-claude-proxy.test.js
9274
+ ensureClaudeProcessProxyRunning,
9275
+ probeLocalPort
9093
9276
  };
9094
9277
  }
9095
9278
  });
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@simonyea/holysheep-cli",
3
- "version": "2.1.42",
3
+ "version": "2.1.44",
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/opencode-default-model.test.js && node tests/paths-bundled.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/opencode-default-model.test.js && node tests/paths-bundled.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",
8
8
  "prepublishOnly": "npm run build && npm test && node scripts/check-tarball-size.js"
9
9
  },
10
10
  "keywords": [