@simonyea/holysheep-cli 2.1.51 → 2.1.53

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.
@@ -1399,7 +1399,7 @@ var require_codex = __commonJS({
1399
1399
  }
1400
1400
  const content = stripManagedTomlConfig(readTomlConfig());
1401
1401
  const newConfig = [
1402
- `model = "${model || "gpt-5.4"}"`,
1402
+ `model = "${model || "gpt-5.5"}"`,
1403
1403
  `model_provider = "holysheep"`,
1404
1404
  "",
1405
1405
  content,
@@ -1422,7 +1422,7 @@ var require_codex = __commonJS({
1422
1422
  if (fs.existsSync(CONFIG_FILE_JSON)) {
1423
1423
  jsonConfig = JSON.parse(fs.readFileSync(CONFIG_FILE_JSON, "utf8"));
1424
1424
  }
1425
- jsonConfig.model = model || "gpt-5.4";
1425
+ jsonConfig.model = model || "gpt-5.5";
1426
1426
  jsonConfig.model_provider = "holysheep";
1427
1427
  jsonConfig.provider = "holysheep";
1428
1428
  if (!jsonConfig.model_providers) jsonConfig.model_providers = {};
@@ -1479,7 +1479,7 @@ var require_codex = __commonJS({
1479
1479
  return isConfiguredInToml();
1480
1480
  },
1481
1481
  configure(apiKey, _baseUrlAnthropicNoV1, baseUrlOpenAI) {
1482
- const model = "gpt-5.4";
1482
+ const model = "gpt-5.5";
1483
1483
  writeTomlConfig(apiKey, baseUrlOpenAI, model);
1484
1484
  writeJsonConfigIfNeeded(apiKey, baseUrlOpenAI, model);
1485
1485
  neutralizeAuthJson();
@@ -1995,7 +1995,13 @@ var require_openclaw_bridge = __commonJS({
1995
1995
  function sendJson(res, statusCode, payload) {
1996
1996
  res.writeHead(statusCode, {
1997
1997
  "content-type": "application/json; charset=utf-8",
1998
- "cache-control": "no-store"
1998
+ "cache-control": "no-store",
1999
+ // [hs36] Mirror the OPTIONS preflight ACAO so browsers can read the
2000
+ // response from a different-port origin (e.g. the AionUi WebUI on :9876
2001
+ // fetching /v1/models from the bridge on :18788). Without this header on
2002
+ // the actual GET/POST response, Chrome/Firefox treat it as ERR_FAILED
2003
+ // even though the OPTIONS preflight succeeds.
2004
+ "access-control-allow-origin": "*"
1999
2005
  });
2000
2006
  res.end(JSON.stringify(payload));
2001
2007
  }
@@ -2009,7 +2015,10 @@ var require_openclaw_bridge = __commonJS({
2009
2015
  res.writeHead(200, {
2010
2016
  "content-type": "text/event-stream; charset=utf-8",
2011
2017
  "cache-control": "no-cache, no-transform",
2012
- connection: "keep-alive"
2018
+ connection: "keep-alive",
2019
+ // [hs36] Mirror sendJson's ACAO so SSE streams reach the WebUI from a
2020
+ // cross-port loopback origin. See sendJson comment for rationale.
2021
+ "access-control-allow-origin": "*"
2013
2022
  });
2014
2023
  const firstChunk = {
2015
2024
  id: payload.id,
@@ -2429,7 +2438,9 @@ var require_openclaw_bridge = __commonJS({
2429
2438
  res.writeHead(200, {
2430
2439
  "content-type": "text/event-stream; charset=utf-8",
2431
2440
  "cache-control": "no-cache, no-transform",
2432
- connection: "keep-alive"
2441
+ connection: "keep-alive",
2442
+ // [hs36] CORS for SSE byte-passthrough relay path.
2443
+ "access-control-allow-origin": "*"
2433
2444
  });
2434
2445
  try {
2435
2446
  await pipeStream(upstream.body, (chunk) => res.write(chunk));
@@ -2503,7 +2514,10 @@ var require_openclaw_bridge = __commonJS({
2503
2514
  res.writeHead(200, {
2504
2515
  "content-type": "text/event-stream; charset=utf-8",
2505
2516
  "cache-control": "no-cache, no-transform",
2506
- connection: "keep-alive"
2517
+ connection: "keep-alive",
2518
+ // [hs36] Mirror sendJson's ACAO so SSE streams reach the WebUI from a
2519
+ // cross-port loopback origin. See sendJson comment for rationale.
2520
+ "access-control-allow-origin": "*"
2507
2521
  });
2508
2522
  const msgId = `chatcmpl_${Date.now()}`;
2509
2523
  const created = Math.floor(Date.now() / 1e3);
@@ -3723,6 +3737,18 @@ var require_hermes = __commonJS({
3723
3737
  __name(readConfig, "readConfig");
3724
3738
  function patchConfigYaml(apiKey, baseUrlOpenAI, primaryModel) {
3725
3739
  if (!fs.existsSync(CONFIG_YAML)) {
3740
+ const cleanBase2 = String(baseUrlOpenAI || "https://api.holysheep.ai/v1").replace(/\/+$/, "");
3741
+ const model2 = primaryModel || "claude-sonnet-4-6";
3742
+ const minimal = `model:
3743
+ base_url: ${cleanBase2}
3744
+ api_key: ${apiKey}
3745
+ default: ${model2}
3746
+ providers:
3747
+ custom:
3748
+ base_url: ${cleanBase2}
3749
+ api_key: ${apiKey}
3750
+ `;
3751
+ fs.writeFileSync(CONFIG_YAML, minimal, "utf8");
3726
3752
  return;
3727
3753
  }
3728
3754
  const cleanBase = String(baseUrlOpenAI || "https://api.holysheep.ai/v1").replace(/\/+$/, "");
@@ -3766,7 +3792,6 @@ var require_hermes = __commonJS({
3766
3792
  " custom:",
3767
3793
  ` base_url: ${cleanBase}`,
3768
3794
  ` api_key: ${apiKey}`,
3769
- ` default_model: ${model}`,
3770
3795
  " type: openai"
3771
3796
  ];
3772
3797
  let start = -1, end = -1;
@@ -3879,7 +3904,7 @@ var require_hermes = __commonJS({
3879
3904
  __name(writeConfig, "writeConfig");
3880
3905
  function buildHolySheepBlock(apiKey, baseUrlOpenAI, primaryModel) {
3881
3906
  const cleanBase = String(baseUrlOpenAI || "https://api.holysheep.ai/v1").replace(/\/+$/, "");
3882
- const model = primaryModel || "gpt-5.4";
3907
+ const model = primaryModel || "gpt-4o";
3883
3908
  return [
3884
3909
  "[providers.holysheep]",
3885
3910
  "# Managed by @simonyea/holysheep-cli. Do not edit by hand \u2014 run `hs setup`.",
@@ -3946,10 +3971,11 @@ var require_hermes = __commonJS({
3946
3971
  return commandExists("hermes");
3947
3972
  },
3948
3973
  isConfigured() {
3949
- return isConfiguredInToml(readConfig());
3974
+ return isConfiguredInToml(readConfig()) && fs.existsSync(CONFIG_YAML);
3950
3975
  },
3951
3976
  configure(apiKey, _baseUrlAnthropic, baseUrlOpenAI, primaryModel) {
3952
- const hermesPrimaryModel = /^claude-/i.test(primaryModel || "") ? "gpt-5.4" : primaryModel || "gpt-5.4";
3977
+ const THINKING_MODELS = /^(gpt-5|o[0-9]|claude-)/i;
3978
+ const hermesPrimaryModel = THINKING_MODELS.test(primaryModel || "") ? "gpt-4o" : primaryModel || "gpt-4o";
3953
3979
  const merged = mergeConfig(apiKey, baseUrlOpenAI, hermesPrimaryModel);
3954
3980
  writeConfig(merged);
3955
3981
  try {
@@ -3993,11 +4019,11 @@ var require_package = __commonJS({
3993
4019
  "package.json"(exports2, module2) {
3994
4020
  module2.exports = {
3995
4021
  name: "@simonyea/holysheep-cli",
3996
- version: "2.1.51",
4022
+ version: "2.1.53",
3997
4023
  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.",
3998
4024
  scripts: {
3999
4025
  build: "node scripts/build.mjs",
4000
- 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-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/version-check.test.js && node tests/runclaude-missing-binary.test.js",
4026
+ 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-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/version-check.test.js && node tests/runclaude-missing-binary.test.js",
4001
4027
  prepublishOnly: "npm run build && npm test && node scripts/check-tarball-size.js"
4002
4028
  },
4003
4029
  keywords: [
@@ -4445,6 +4471,7 @@ var require_env_config = __commonJS({
4445
4471
  module2.exports = {
4446
4472
  name: "\u5168\u5C40\u73AF\u5883\u53D8\u91CF",
4447
4473
  id: "env-config",
4474
+ installCmd: "\u5185\u7F6E\u529F\u80FD\uFF08\u65E0\u9700\u5B89\u88C5\uFF09",
4448
4475
  checkInstalled() {
4449
4476
  return true;
4450
4477
  },
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.51",
15
+ version: "2.1.53",
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 && 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/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/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/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: [
@@ -1945,7 +1945,7 @@ var require_codex = __commonJS({
1945
1945
  }
1946
1946
  const content = stripManagedTomlConfig(readTomlConfig());
1947
1947
  const newConfig = [
1948
- `model = "${model || "gpt-5.4"}"`,
1948
+ `model = "${model || "gpt-5.5"}"`,
1949
1949
  `model_provider = "holysheep"`,
1950
1950
  "",
1951
1951
  content,
@@ -1968,7 +1968,7 @@ var require_codex = __commonJS({
1968
1968
  if (fs.existsSync(CONFIG_FILE_JSON)) {
1969
1969
  jsonConfig = JSON.parse(fs.readFileSync(CONFIG_FILE_JSON, "utf8"));
1970
1970
  }
1971
- jsonConfig.model = model || "gpt-5.4";
1971
+ jsonConfig.model = model || "gpt-5.5";
1972
1972
  jsonConfig.model_provider = "holysheep";
1973
1973
  jsonConfig.provider = "holysheep";
1974
1974
  if (!jsonConfig.model_providers) jsonConfig.model_providers = {};
@@ -2025,7 +2025,7 @@ var require_codex = __commonJS({
2025
2025
  return isConfiguredInToml();
2026
2026
  },
2027
2027
  configure(apiKey, _baseUrlAnthropicNoV1, baseUrlOpenAI) {
2028
- const model = "gpt-5.4";
2028
+ const model = "gpt-5.5";
2029
2029
  writeTomlConfig(apiKey, baseUrlOpenAI, model);
2030
2030
  writeJsonConfigIfNeeded(apiKey, baseUrlOpenAI, model);
2031
2031
  neutralizeAuthJson();
@@ -2541,7 +2541,13 @@ var require_openclaw_bridge = __commonJS({
2541
2541
  function sendJson(res, statusCode, payload) {
2542
2542
  res.writeHead(statusCode, {
2543
2543
  "content-type": "application/json; charset=utf-8",
2544
- "cache-control": "no-store"
2544
+ "cache-control": "no-store",
2545
+ // [hs36] Mirror the OPTIONS preflight ACAO so browsers can read the
2546
+ // response from a different-port origin (e.g. the AionUi WebUI on :9876
2547
+ // fetching /v1/models from the bridge on :18788). Without this header on
2548
+ // the actual GET/POST response, Chrome/Firefox treat it as ERR_FAILED
2549
+ // even though the OPTIONS preflight succeeds.
2550
+ "access-control-allow-origin": "*"
2545
2551
  });
2546
2552
  res.end(JSON.stringify(payload));
2547
2553
  }
@@ -2555,7 +2561,10 @@ var require_openclaw_bridge = __commonJS({
2555
2561
  res.writeHead(200, {
2556
2562
  "content-type": "text/event-stream; charset=utf-8",
2557
2563
  "cache-control": "no-cache, no-transform",
2558
- connection: "keep-alive"
2564
+ connection: "keep-alive",
2565
+ // [hs36] Mirror sendJson's ACAO so SSE streams reach the WebUI from a
2566
+ // cross-port loopback origin. See sendJson comment for rationale.
2567
+ "access-control-allow-origin": "*"
2559
2568
  });
2560
2569
  const firstChunk = {
2561
2570
  id: payload.id,
@@ -2975,7 +2984,9 @@ var require_openclaw_bridge = __commonJS({
2975
2984
  res.writeHead(200, {
2976
2985
  "content-type": "text/event-stream; charset=utf-8",
2977
2986
  "cache-control": "no-cache, no-transform",
2978
- connection: "keep-alive"
2987
+ connection: "keep-alive",
2988
+ // [hs36] CORS for SSE byte-passthrough relay path.
2989
+ "access-control-allow-origin": "*"
2979
2990
  });
2980
2991
  try {
2981
2992
  await pipeStream(upstream.body, (chunk) => res.write(chunk));
@@ -3049,7 +3060,10 @@ var require_openclaw_bridge = __commonJS({
3049
3060
  res.writeHead(200, {
3050
3061
  "content-type": "text/event-stream; charset=utf-8",
3051
3062
  "cache-control": "no-cache, no-transform",
3052
- connection: "keep-alive"
3063
+ connection: "keep-alive",
3064
+ // [hs36] Mirror sendJson's ACAO so SSE streams reach the WebUI from a
3065
+ // cross-port loopback origin. See sendJson comment for rationale.
3066
+ "access-control-allow-origin": "*"
3053
3067
  });
3054
3068
  const msgId = `chatcmpl_${Date.now()}`;
3055
3069
  const created = Math.floor(Date.now() / 1e3);
@@ -4269,6 +4283,18 @@ var require_hermes = __commonJS({
4269
4283
  __name(readConfig, "readConfig");
4270
4284
  function patchConfigYaml(apiKey, baseUrlOpenAI, primaryModel) {
4271
4285
  if (!fs.existsSync(CONFIG_YAML)) {
4286
+ const cleanBase2 = String(baseUrlOpenAI || "https://api.holysheep.ai/v1").replace(/\/+$/, "");
4287
+ const model2 = primaryModel || "claude-sonnet-4-6";
4288
+ const minimal = `model:
4289
+ base_url: ${cleanBase2}
4290
+ api_key: ${apiKey}
4291
+ default: ${model2}
4292
+ providers:
4293
+ custom:
4294
+ base_url: ${cleanBase2}
4295
+ api_key: ${apiKey}
4296
+ `;
4297
+ fs.writeFileSync(CONFIG_YAML, minimal, "utf8");
4272
4298
  return;
4273
4299
  }
4274
4300
  const cleanBase = String(baseUrlOpenAI || "https://api.holysheep.ai/v1").replace(/\/+$/, "");
@@ -4312,7 +4338,6 @@ var require_hermes = __commonJS({
4312
4338
  " custom:",
4313
4339
  ` base_url: ${cleanBase}`,
4314
4340
  ` api_key: ${apiKey}`,
4315
- ` default_model: ${model}`,
4316
4341
  " type: openai"
4317
4342
  ];
4318
4343
  let start = -1, end = -1;
@@ -4425,7 +4450,7 @@ var require_hermes = __commonJS({
4425
4450
  __name(writeConfig, "writeConfig");
4426
4451
  function buildHolySheepBlock(apiKey, baseUrlOpenAI, primaryModel) {
4427
4452
  const cleanBase = String(baseUrlOpenAI || "https://api.holysheep.ai/v1").replace(/\/+$/, "");
4428
- const model = primaryModel || "gpt-5.4";
4453
+ const model = primaryModel || "gpt-4o";
4429
4454
  return [
4430
4455
  "[providers.holysheep]",
4431
4456
  "# Managed by @simonyea/holysheep-cli. Do not edit by hand \u2014 run `hs setup`.",
@@ -4492,10 +4517,11 @@ var require_hermes = __commonJS({
4492
4517
  return commandExists2("hermes");
4493
4518
  },
4494
4519
  isConfigured() {
4495
- return isConfiguredInToml(readConfig());
4520
+ return isConfiguredInToml(readConfig()) && fs.existsSync(CONFIG_YAML);
4496
4521
  },
4497
4522
  configure(apiKey, _baseUrlAnthropic, baseUrlOpenAI, primaryModel) {
4498
- const hermesPrimaryModel = /^claude-/i.test(primaryModel || "") ? "gpt-5.4" : primaryModel || "gpt-5.4";
4523
+ const THINKING_MODELS = /^(gpt-5|o[0-9]|claude-)/i;
4524
+ const hermesPrimaryModel = THINKING_MODELS.test(primaryModel || "") ? "gpt-4o" : primaryModel || "gpt-4o";
4499
4525
  const merged = mergeConfig(apiKey, baseUrlOpenAI, hermesPrimaryModel);
4500
4526
  writeConfig(merged);
4501
4527
  try {
@@ -4627,6 +4653,7 @@ var require_env_config = __commonJS({
4627
4653
  module2.exports = {
4628
4654
  name: "\u5168\u5C40\u73AF\u5883\u53D8\u91CF",
4629
4655
  id: "env-config",
4656
+ installCmd: "\u5185\u7F6E\u529F\u80FD\uFF08\u65E0\u9700\u5B89\u88C5\uFF09",
4630
4657
  checkInstalled() {
4631
4658
  return true;
4632
4659
  },
@@ -8802,6 +8829,7 @@ var require_aionui_wrapper = __commonJS({
8802
8829
  var codexTool = require_codex();
8803
8830
  var droidTool = require_droid();
8804
8831
  var hermesTool = require_hermes();
8832
+ var openclawTool = require_openclaw();
8805
8833
  var BRIDGE_DIR = path.join(os.homedir(), ".holysheep");
8806
8834
  var BRIDGE_CRED_FILE = path.join(BRIDGE_DIR, "aionui-bridge.json");
8807
8835
  var TOKEN_TTL_MS = 3e4;
@@ -8821,6 +8849,11 @@ var require_aionui_wrapper = __commonJS({
8821
8849
  var cachedAionUiCookie = null;
8822
8850
  var cachedAionUiCookieAt = 0;
8823
8851
  var AIONUI_COOKIE_TTL_MS = 10 * 60 * 1e3;
8852
+ var _startupWarnings = {
8853
+ notInstalled: [],
8854
+ // tool ids that are installed but not configured
8855
+ openclawNeedsPairing: false
8856
+ };
8824
8857
  function log(msg) {
8825
8858
  console.log(`[holysheep-web] ${msg}`);
8826
8859
  }
@@ -9114,6 +9147,90 @@ var require_aionui_wrapper = __commonJS({
9114
9147
  return legacyModule;
9115
9148
  }
9116
9149
  __name(legacy, "legacy");
9150
+ var _cachedOpenClawOrigins = null;
9151
+ var _cachedOpenClawOriginsAt = 0;
9152
+ var OPENCLAW_ORIGIN_TTL_MS = 6e4;
9153
+ function getOpenClawOrigins() {
9154
+ if (_cachedOpenClawOrigins && nowMs() - _cachedOpenClawOriginsAt < OPENCLAW_ORIGIN_TTL_MS) {
9155
+ return _cachedOpenClawOrigins;
9156
+ }
9157
+ const origins = [];
9158
+ try {
9159
+ const bridgePort = openclawTool.getBridgePort();
9160
+ const gatewayPort = openclawTool.getGatewayPort();
9161
+ if (bridgePort) origins.push(`http://127.0.0.1:${bridgePort}`);
9162
+ if (gatewayPort) origins.push(`http://127.0.0.1:${gatewayPort}`);
9163
+ } catch {
9164
+ }
9165
+ _cachedOpenClawOrigins = origins;
9166
+ _cachedOpenClawOriginsAt = nowMs();
9167
+ return origins;
9168
+ }
9169
+ __name(getOpenClawOrigins, "getOpenClawOrigins");
9170
+ function appendOriginsToDirective(directiveValue, origins) {
9171
+ const tokens = new Set(directiveValue.trim().split(/\s+/).filter(Boolean));
9172
+ for (const o of origins) tokens.add(o);
9173
+ return Array.from(tokens).join(" ");
9174
+ }
9175
+ __name(appendOriginsToDirective, "appendOriginsToDirective");
9176
+ function rewriteCspHeaders(outHeaders) {
9177
+ const origins = getOpenClawOrigins();
9178
+ if (origins.length === 0) return;
9179
+ for (const key of Object.keys(outHeaders)) {
9180
+ const lower = key.toLowerCase();
9181
+ if (lower !== "content-security-policy" && lower !== "content-security-policy-report-only") {
9182
+ continue;
9183
+ }
9184
+ const value = outHeaders[key];
9185
+ const cspString = Array.isArray(value) ? value.join("; ") : String(value || "");
9186
+ if (!cspString) continue;
9187
+ const directives = cspString.split(";").map((s) => s.trim()).filter(Boolean);
9188
+ let foundConnect = false;
9189
+ const rewritten = directives.map((d) => {
9190
+ const m = d.match(/^(connect-src)\s+(.*)$/i);
9191
+ if (!m) return d;
9192
+ foundConnect = true;
9193
+ return `connect-src ${appendOriginsToDirective(m[2], origins)}`;
9194
+ });
9195
+ if (!foundConnect) {
9196
+ rewritten.push(`connect-src 'self' ws: wss: blob: ${origins.join(" ")}`);
9197
+ }
9198
+ outHeaders[key] = rewritten.join("; ");
9199
+ }
9200
+ }
9201
+ __name(rewriteCspHeaders, "rewriteCspHeaders");
9202
+ function _buildStartupBannerScript() {
9203
+ const notInstalled = _startupWarnings.notInstalled || [];
9204
+ const needsPairing = _startupWarnings.openclawNeedsPairing;
9205
+ if (notInstalled.length === 0 && !needsPairing) return "";
9206
+ const lines = [];
9207
+ if (notInstalled.length > 0) {
9208
+ const names = notInstalled.map((id) => {
9209
+ const map = { codex: "Codex", droid: "Droid", hermes: "Hermes", "gemini-cli": "Gemini CLI" };
9210
+ return map[id] || id;
9211
+ });
9212
+ lines.push(`\u26A0\uFE0F \u4EE5\u4E0B CLI \u5DE5\u5177\u672A\u5B89\u88C5\uFF1A${names.join("\u3001")}\u3002\u8BF7\u8FD0\u884C <code>hs setup</code> \u5B89\u88C5\u5E76\u914D\u7F6E\u3002`);
9213
+ }
9214
+ if (needsPairing) {
9215
+ lines.push("\u26A0\uFE0F OpenClaw Gateway \u9700\u8981 Pairing \u8BA4\u8BC1\u3002\u8BF7\u8FD0\u884C <code>hs openclaw</code> \u5B8C\u6210\u914D\u7F6E\uFF0C\u5426\u5219 OpenClaw \u5BF9\u8BDD\u5C06\u6301\u7EED\u62A5\u9519\u3002");
9216
+ }
9217
+ const msgJson = JSON.stringify(lines);
9218
+ return `<script>
9219
+ (function(){
9220
+ var KEY='hs-startup-banner-dismissed';
9221
+ if(sessionStorage.getItem(KEY)) return;
9222
+ var msgs=${msgJson};
9223
+ if(!msgs||!msgs.length) return;
9224
+ var d=document.createElement('div');
9225
+ d.style.cssText='position:fixed;bottom:16px;right:16px;z-index:99999;max-width:420px;background:#ff8c00;color:#fff;padding:12px 16px;border-radius:8px;font-size:13px;line-height:1.5;box-shadow:0 4px 16px rgba(0,0,0,.3);cursor:pointer';
9226
+ d.innerHTML=msgs.join('<br><br>')+'<br><span style="font-size:11px;opacity:.8">\u70B9\u51FB\u5173\u95ED (\u672C\u6B21\u4F1A\u8BDD\u4E0D\u518D\u663E\u793A)</span>';
9227
+ d.addEventListener('click',function(){document.body.removeChild(d);sessionStorage.setItem(KEY,'1')});
9228
+ setTimeout(function(){if(d.parentNode)document.body.removeChild(d);},30000);
9229
+ document.body.appendChild(d);
9230
+ })();
9231
+ </script>`;
9232
+ }
9233
+ __name(_buildStartupBannerScript, "_buildStartupBannerScript");
9117
9234
  var BODYLESS_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
9118
9235
  function proxyHttp(req, res, internalPort) {
9119
9236
  const headers = { ...req.headers };
@@ -9137,6 +9254,33 @@ var require_aionui_wrapper = __commonJS({
9137
9254
  delete outHeaders["connection"];
9138
9255
  delete outHeaders["keep-alive"];
9139
9256
  delete outHeaders["proxy-connection"];
9257
+ rewriteCspHeaders(outHeaders);
9258
+ const contentType = (outHeaders["content-type"] || "").toLowerCase();
9259
+ const isHtml = contentType.includes("text/html");
9260
+ const bannerScript = isHtml ? _buildStartupBannerScript() : "";
9261
+ if (bannerScript) {
9262
+ delete outHeaders["content-length"];
9263
+ res.writeHead(upRes.statusCode, upRes.statusMessage, outHeaders);
9264
+ const chunks = [];
9265
+ upRes.on("data", (c) => chunks.push(c));
9266
+ upRes.on("end", () => {
9267
+ let html = Buffer.concat(chunks).toString("utf8");
9268
+ const idx = html.lastIndexOf("</body>");
9269
+ if (idx !== -1) {
9270
+ html = html.slice(0, idx) + bannerScript + html.slice(idx);
9271
+ } else {
9272
+ html += bannerScript;
9273
+ }
9274
+ res.end(html);
9275
+ });
9276
+ upRes.on("error", () => {
9277
+ try {
9278
+ res.end();
9279
+ } catch {
9280
+ }
9281
+ });
9282
+ return;
9283
+ }
9140
9284
  res.writeHead(upRes.statusCode, upRes.statusMessage, outHeaders);
9141
9285
  upRes.pipe(res);
9142
9286
  });
@@ -9317,6 +9461,12 @@ var require_aionui_wrapper = __commonJS({
9317
9461
  aionuiSource: ctx.runtimeSource
9318
9462
  });
9319
9463
  }
9464
+ if (route === "/api/holysheep/__wrapper/startup-warnings" && req.method === "GET") {
9465
+ return sendJson(res, 200, {
9466
+ notInstalled: _startupWarnings.notInstalled,
9467
+ openclawNeedsPairing: _startupWarnings.openclawNeedsPairing
9468
+ });
9469
+ }
9320
9470
  return proxyHttp(req, res, ctx.internalPort);
9321
9471
  } catch (e) {
9322
9472
  try {
@@ -9398,7 +9548,7 @@ var require_aionui_wrapper = __commonJS({
9398
9548
  log("skipping auto-config for all CLI tools: no HolySheep apiKey available");
9399
9549
  return;
9400
9550
  }
9401
- const summary = { configured: [], skipped: [], failed: [] };
9551
+ const summary = { configured: [], skipped: [], failed: [], notInstalled: [] };
9402
9552
  try {
9403
9553
  _ensureClaudeProxyConfig(apiKey);
9404
9554
  summary.configured.push("claude-code");
@@ -9434,6 +9584,17 @@ var require_aionui_wrapper = __commonJS({
9434
9584
  ];
9435
9585
  for (const s of siblings) {
9436
9586
  try {
9587
+ let installed = true;
9588
+ try {
9589
+ installed = typeof s.tool.checkInstalled === "function" ? Boolean(s.tool.checkInstalled()) : true;
9590
+ } catch {
9591
+ installed = true;
9592
+ }
9593
+ if (!installed) {
9594
+ summary.notInstalled.push(s.id);
9595
+ log(`${s.id} not installed \u2014 skipping auto-config (binary not found)`);
9596
+ continue;
9597
+ }
9437
9598
  let already = false;
9438
9599
  try {
9439
9600
  already = typeof s.tool.isConfigured === "function" ? Boolean(s.tool.isConfigured()) : false;
@@ -9458,10 +9619,30 @@ var require_aionui_wrapper = __commonJS({
9458
9619
  log(`warn: auto-config ${s.id} failed: ${err && err.message ? err.message : err}`);
9459
9620
  }
9460
9621
  }
9461
- log(`CLI auto-config summary: configured=[${summary.configured.join(",")}] skipped=[${summary.skipped.join(",")}] failed=[${summary.failed.map((f) => f.tool).join(",")}]`);
9622
+ log(`CLI auto-config summary: configured=[${summary.configured.join(",")}] skipped=[${summary.skipped.join(",")}] failed=[${summary.failed.map((f) => f.tool).join(",")}] notInstalled=[${summary.notInstalled.join(",")}]`);
9462
9623
  return summary;
9463
9624
  }
9464
9625
  __name(_ensureAllClisConfigured, "_ensureAllClisConfigured");
9626
+ function _checkOpenClawPairingNeeded() {
9627
+ var _a, _b;
9628
+ try {
9629
+ if (typeof openclawTool.checkInstalled !== "function" || !openclawTool.checkInstalled()) return false;
9630
+ if (typeof openclawTool.isConfigured !== "function" || !openclawTool.isConfigured()) return false;
9631
+ const OPENCLAW_CONFIG_FILE = path.join(os.homedir(), ".openclaw", "openclaw.json");
9632
+ if (!fs.existsSync(OPENCLAW_CONFIG_FILE)) return false;
9633
+ let cfg;
9634
+ try {
9635
+ cfg = JSON.parse(fs.readFileSync(OPENCLAW_CONFIG_FILE, "utf8"));
9636
+ } catch {
9637
+ return false;
9638
+ }
9639
+ const authMode = (_b = (_a = cfg == null ? void 0 : cfg.gateway) == null ? void 0 : _a.auth) == null ? void 0 : _b.mode;
9640
+ return authMode !== void 0 && authMode !== "none";
9641
+ } catch {
9642
+ return false;
9643
+ }
9644
+ }
9645
+ __name(_checkOpenClawPairingNeeded, "_checkOpenClawPairingNeeded");
9465
9646
  function _ensureClaudeProxyConfig(apiKey) {
9466
9647
  const config = claudeProcessProxy.readConfig();
9467
9648
  const next = claudeCodeTool.buildBridgeConfig(apiKey, BASE_URL_ANTHROPIC, {
@@ -9527,6 +9708,48 @@ var require_aionui_wrapper = __commonJS({
9527
9708
  }
9528
9709
  }
9529
9710
  __name(ensureClaudeProcessProxyRunning, "ensureClaudeProcessProxyRunning");
9711
+ function ensureOpenClawBridgeRunning() {
9712
+ let bridgePort;
9713
+ try {
9714
+ if (typeof openclawTool.isConfigured === "function" && !openclawTool.isConfigured()) {
9715
+ log("OpenClaw bridge pre-start skipped: openclaw not configured (run `hs setup` to enable)");
9716
+ return { skipped: true, reason: "not-configured" };
9717
+ }
9718
+ bridgePort = openclawTool.getBridgePort();
9719
+ } catch (e) {
9720
+ log(`OpenClaw bridge pre-start skipped: cannot read openclaw config (${e && e.message ? e.message : e})`);
9721
+ return { skipped: true, reason: "config-read-failed" };
9722
+ }
9723
+ try {
9724
+ disableBridgeWatchdog();
9725
+ } catch (e) {
9726
+ log(`warn: failed to disable bridge watchdog (continuing): ${e && e.message ? e.message : e}`);
9727
+ }
9728
+ try {
9729
+ const ok = openclawTool.ensureBridgeRunning(bridgePort);
9730
+ if (ok) {
9731
+ log(`OpenClaw bridge ready on 127.0.0.1:${bridgePort} (watchdog disabled \u2014 gateway-less mode)`);
9732
+ return { started: true, port: bridgePort };
9733
+ }
9734
+ log(`OpenClaw bridge failed to start on :${bridgePort} \u2014 OpenClaw chat panel will be unavailable`);
9735
+ return { skipped: true, reason: "start-failed", port: bridgePort };
9736
+ } catch (e) {
9737
+ log(`OpenClaw bridge pre-start error (continuing): ${e && e.message ? e.message : e}`);
9738
+ return { skipped: true, reason: "spawn-error" };
9739
+ }
9740
+ }
9741
+ __name(ensureOpenClawBridgeRunning, "ensureOpenClawBridgeRunning");
9742
+ function disableBridgeWatchdog() {
9743
+ const { BRIDGE_CONFIG_FILE } = require_openclaw_bridge();
9744
+ if (!fs.existsSync(BRIDGE_CONFIG_FILE)) return;
9745
+ const raw = JSON.parse(fs.readFileSync(BRIDGE_CONFIG_FILE, "utf8"));
9746
+ if (!raw.watchdog) raw.watchdog = {};
9747
+ if (raw.watchdog.enabled === false && raw.gatewayPid == null) return;
9748
+ raw.watchdog.enabled = false;
9749
+ raw.gatewayPid = null;
9750
+ fs.writeFileSync(BRIDGE_CONFIG_FILE, JSON.stringify(raw, null, 2), "utf8");
9751
+ }
9752
+ __name(disableBridgeWatchdog, "disableBridgeWatchdog");
9530
9753
  async function startWrapper({ port, runtimeDir, runtimeVersion, runtimeSource, bunPath }) {
9531
9754
  ensureRuntimeBuiltinResources(runtimeDir);
9532
9755
  const hsNative = detectHolySheepAionUi(runtimeDir);
@@ -9544,12 +9767,27 @@ var require_aionui_wrapper = __commonJS({
9544
9767
  return null;
9545
9768
  }
9546
9769
  })();
9547
- if (__apiKeyForAutoConfig) _ensureAllClisConfigured(__apiKeyForAutoConfig);
9548
- else log("CLI auto-config skipped: no HolySheep apiKey (run `hs login`)");
9770
+ if (__apiKeyForAutoConfig) {
9771
+ const summary = _ensureAllClisConfigured(__apiKeyForAutoConfig);
9772
+ if (summary && summary.notInstalled && summary.notInstalled.length > 0) {
9773
+ _startupWarnings.notInstalled = summary.notInstalled;
9774
+ }
9775
+ } else {
9776
+ log("CLI auto-config skipped: no HolySheep apiKey (run `hs login`)");
9777
+ }
9549
9778
  } catch (e) {
9550
9779
  log("warn: CLI auto-config error (continuing): " + (e && e.message ? e.message : e));
9551
9780
  }
9781
+ try {
9782
+ _startupWarnings.openclawNeedsPairing = _checkOpenClawPairingNeeded();
9783
+ if (_startupWarnings.openclawNeedsPairing) {
9784
+ log('warn: OpenClaw gateway auth.mode is not "none" \u2014 users will see "pairing required" errors. Run `hs openclaw` to fix.');
9785
+ }
9786
+ } catch (e) {
9787
+ log("warn: OpenClaw pairing check failed (continuing): " + (e && e.message ? e.message : e));
9788
+ }
9552
9789
  const claudeProxyHandle = await ensureClaudeProcessProxyRunning();
9790
+ ensureOpenClawBridgeRunning();
9553
9791
  const debug = process.env.HS_WEB_DEBUG === "1";
9554
9792
  const { PACKAGE_ROOT, cliVersion, ptyHermesWrapperPath } = require_paths();
9555
9793
  const cliRoot = PACKAGE_ROOT;
@@ -9700,7 +9938,12 @@ ${tail.split(/\r?\n/).map((ln) => ` ${ln}`).join("\n")}
9700
9938
  ensureRuntimeBuiltinResources,
9701
9939
  // [HolySheep v2.1.43] Exported for aionui-wrapper-claude-proxy.test.js
9702
9940
  ensureClaudeProcessProxyRunning,
9703
- probeLocalPort
9941
+ probeLocalPort,
9942
+ // [HolySheep v2.1.51 / hs36] Exported for tests
9943
+ ensureOpenClawBridgeRunning,
9944
+ rewriteCspHeaders,
9945
+ getOpenClawOrigins,
9946
+ appendOriginsToDirective
9704
9947
  };
9705
9948
  }
9706
9949
  });
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@simonyea/holysheep-cli",
3
- "version": "2.1.51",
3
+ "version": "2.1.53",
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 && 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/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/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/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": [