@simonyea/holysheep-cli 2.1.69 → 2.1.70

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.
@@ -4135,11 +4135,11 @@ var require_package = __commonJS({
4135
4135
  "package.json"(exports2, module2) {
4136
4136
  module2.exports = {
4137
4137
  name: "@simonyea/holysheep-cli",
4138
- version: "2.1.69",
4138
+ version: "2.1.70",
4139
4139
  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
4140
  scripts: {
4141
4141
  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",
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",
4143
4143
  prepublishOnly: "npm run build && npm test && node scripts/check-tarball-size.js"
4144
4144
  },
4145
4145
  keywords: [
package/dist/index.html CHANGED
@@ -584,7 +584,7 @@ button { cursor: pointer; }
584
584
  <button class="nav-item" data-route="dashboard"><span class="nav-dot"></span><span>Dashboard</span></button>
585
585
  <button class="nav-item" data-route="workspace"><span class="nav-dot"></span><span>Conversations</span></button>
586
586
  <button class="nav-item" data-route="tasks"><span class="nav-dot"></span><span>Scheduled Tasks</span></button>
587
- <button class="nav-item" data-route="tools"><span class="nav-dot"></span><span>Tools & MCP</span></button>
587
+ <button class="nav-item" data-route="tools"><span class="nav-dot"></span><span>CLI 管理</span></button>
588
588
  <button class="nav-item" data-route="account"><span class="nav-dot"></span><span>HolySheep Account</span></button>
589
589
  </div>
590
590
  <div class="sidebar-card">
@@ -646,8 +646,8 @@ const pageMeta = {
646
646
  subtitle: 'Manage recurring prompts and maintain the default HolySheep API config directly below the task list.',
647
647
  },
648
648
  tools: {
649
- title: 'Tools & MCP',
650
- subtitle: 'Inspect local CLI adapters, launch tools, and run one-click configuration from the same shell.',
649
+ title: 'CLI 管理',
650
+ subtitle: 'Manage local AI CLI adapters, MCP, and the VS Code Claude proxy from one place.',
651
651
  },
652
652
  account: {
653
653
  title: 'HolySheep Account',
@@ -679,16 +679,81 @@ function formatDate(value) {
679
679
  return date.toLocaleString()
680
680
  }
681
681
 
682
- function api(path, options) {
683
- return fetch(path.startsWith('/api/') ? path : `/api/${path}`, options).then(async (response) => {
682
+ function contentTypeOf(response) {
683
+ return (response.headers.get('content-type') || '').toLowerCase()
684
+ }
685
+
686
+ async function parseResponseBody(response) {
687
+ const contentType = contentTypeOf(response)
688
+ if (contentType.includes('text/event-stream')) {
689
+ const text = await response.text()
690
+ return { kind: 'sse', events: parseSseText(text), text }
691
+ }
692
+ if (contentType.includes('application/json')) {
684
693
  const payload = await response.json().catch(() => ({}))
685
- if (!response.ok) {
686
- const error = new Error(payload.error || payload.message || `HTTP ${response.status}`)
687
- error.payload = payload
688
- throw error
694
+ return { kind: 'json', payload }
695
+ }
696
+ const text = await response.text().catch(() => '')
697
+ return { kind: 'text', text }
698
+ }
699
+
700
+ function parseSseText(text) {
701
+ return String(text || '').split('\n\n').map((chunk) => {
702
+ const lines = chunk.split('\n').filter((line) => line.startsWith('data:'))
703
+ if (!lines.length) return null
704
+ const data = lines.map((line) => line.slice(5).trim()).join('\n')
705
+ try { return JSON.parse(data) }
706
+ catch { return { type: 'output', text: data } }
707
+ }).filter(Boolean)
708
+ }
709
+
710
+ async function api(path, options) {
711
+ const response = await fetch(path.startsWith('/api/') ? path : `/api/${path}`, options)
712
+ const parsed = await parseResponseBody(response)
713
+ const payload = parsed.kind === 'json' ? parsed.payload : parsed
714
+ if (!response.ok) {
715
+ const error = new Error(payload.error || payload.message || payload.text || `HTTP ${response.status}`)
716
+ error.payload = payload
717
+ throw error
718
+ }
719
+ return payload
720
+ }
721
+
722
+ function appendSseEventToConsole(payload) {
723
+ if (payload.type === 'output') appendConsole(payload.text || '', '')
724
+ else if (payload.type === 'progress') appendConsole(payload.message || JSON.stringify(payload), payload.status === 'error' ? 'error' : payload.status === 'ok' ? 'ok' : 'warn')
725
+ else if (payload.type === 'done') appendConsole(payload.message || 'Done.', payload.success === false ? 'error' : 'ok')
726
+ else appendConsole(JSON.stringify(payload), '')
727
+ }
728
+
729
+ async function postMaybeSse(path, body, title) {
730
+ openConsole(title)
731
+ const response = await fetch(path.startsWith('/api/') ? path : `/api/${path}`, {
732
+ method: 'POST',
733
+ headers: { 'Content-Type': 'application/json' },
734
+ body: JSON.stringify(body || {}),
735
+ })
736
+ const contentType = contentTypeOf(response)
737
+ if (contentType.includes('text/event-stream')) {
738
+ const events = await consumeSseResponse(response)
739
+ const failed = events.some((event) => event.type === 'done' && event.success === false)
740
+ if (!response.ok || failed) {
741
+ const lastError = [...events].reverse().find((event) => event.error || event.message)
742
+ throw new Error(lastError?.error || lastError?.message || `HTTP ${response.status}`)
743
+ }
744
+ return { kind: 'sse', events }
745
+ }
746
+ const parsed = await parseResponseBody(response)
747
+ if (parsed.kind === 'json') {
748
+ const payload = parsed.payload
749
+ if (!response.ok || payload.ok === false || payload.success === false) {
750
+ throw new Error(payload.error || payload.message || `HTTP ${response.status}`)
689
751
  }
690
752
  return payload
691
- })
753
+ }
754
+ if (!response.ok) throw new Error(parsed.text || `HTTP ${response.status}`)
755
+ if (parsed.text) appendConsole(parsed.text, '')
756
+ return parsed
692
757
  }
693
758
 
694
759
  async function loadBaseState() {
@@ -812,7 +877,7 @@ function renderDashboard() {
812
877
  </div>
813
878
  <div style="margin-top:16px;" class="inline-actions">
814
879
  <button class="btn primary" onclick="setRoute('tasks')">Open Scheduled Tasks</button>
815
- <button class="btn" onclick="setRoute('tools')">Manage CLI tools</button>
880
+ <button class="btn" onclick="setRoute('tools')">打开 CLI 管理</button>
816
881
  </div>
817
882
  </div>
818
883
  </section>
@@ -1054,8 +1119,8 @@ function renderTools() {
1054
1119
  <section class="panel">
1055
1120
  <div class="panel-header">
1056
1121
  <div>
1057
- <h2>Tools & MCP</h2>
1058
- <p>Use the existing HolySheep automation endpoints from inside the new workspace shell.</p>
1122
+ <h2>CLI 管理</h2>
1123
+ <p>统一管理本地 AI CLI、MCP VS Code Claude 代理;代理作为 Claude Code 的子状态展示。</p>
1059
1124
  </div>
1060
1125
  </div>
1061
1126
  <div class="panel-body tool-grid">
@@ -1395,11 +1460,10 @@ async function launchTool(toolId) {
1395
1460
  }
1396
1461
 
1397
1462
  async function startClaudeProxy() {
1398
- openConsole('Start VS Code Claude proxy')
1399
- appendConsole('正在启动 claude-proxy...', 'warn')
1400
1463
  try {
1401
- const result = await api('claude-proxy/start', { method: 'POST' })
1402
- appendConsole(result.message || `代理已启动: 127.0.0.1:${result.port || '?'}`, 'ok')
1464
+ appendConsole('正在启动 claude-proxy...', 'warn')
1465
+ const result = await postMaybeSse('claude-proxy/start', {}, 'Start VS Code Claude proxy')
1466
+ if (result.kind !== 'sse') appendConsole(result.message || `代理已启动: 127.0.0.1:${result.port || '?'}`, 'ok')
1403
1467
  appendConsole('Windows 用户:请完全退出所有 Code.exe,再重新打开 VS Code。', 'warn')
1404
1468
  await loadBaseState()
1405
1469
  render()
@@ -1409,11 +1473,10 @@ async function startClaudeProxy() {
1409
1473
  }
1410
1474
 
1411
1475
  async function stopClaudeProxy() {
1412
- openConsole('Stop VS Code Claude proxy')
1413
- appendConsole('正在停止 claude-proxy...', 'warn')
1414
1476
  try {
1415
- const result = await api('claude-proxy/stop', { method: 'POST' })
1416
- appendConsole(result.message || '代理已停止', 'ok')
1477
+ appendConsole('正在停止 claude-proxy...', 'warn')
1478
+ const result = await postMaybeSse('claude-proxy/stop', {}, 'Stop VS Code Claude proxy')
1479
+ if (result.kind !== 'sse') appendConsole(result.message || '代理已停止', 'ok')
1417
1480
  await loadBaseState()
1418
1481
  render()
1419
1482
  } catch (error) {
@@ -1445,32 +1508,33 @@ document.getElementById('console-close').onclick = () => {
1445
1508
  document.getElementById('console-drawer').classList.remove('open')
1446
1509
  }
1447
1510
 
1448
- async function consumeSse(path, body, title) {
1449
- openConsole(title)
1450
- const response = await fetch(`/api/${path}`, {
1451
- method: 'POST',
1452
- headers: { 'Content-Type': 'application/json' },
1453
- body: JSON.stringify(body),
1454
- })
1511
+ async function consumeSseResponse(response) {
1455
1512
  const reader = response.body.getReader()
1456
1513
  const decoder = new TextDecoder()
1514
+ const events = []
1457
1515
  let buffer = ''
1516
+ async function flush(chunk, final = false) {
1517
+ buffer += chunk
1518
+ const chunks = buffer.split('\n\n')
1519
+ buffer = final ? '' : chunks.pop()
1520
+ for (const part of chunks) {
1521
+ for (const payload of parseSseText(part + '\n\n')) {
1522
+ events.push(payload)
1523
+ appendSseEventToConsole(payload)
1524
+ }
1525
+ }
1526
+ }
1458
1527
  while (true) {
1459
1528
  const { value, done } = await reader.read()
1460
1529
  if (done) break
1461
- buffer += decoder.decode(value, { stream: true })
1462
- const chunks = buffer.split('\n\n')
1463
- buffer = chunks.pop()
1464
- for (const chunk of chunks) {
1465
- const line = chunk.split('\n').find((entry) => entry.startsWith('data:'))
1466
- if (!line) continue
1467
- const payload = JSON.parse(line.slice(5).trim())
1468
- if (payload.type === 'output') appendConsole(payload.text, '')
1469
- else if (payload.type === 'progress') appendConsole(payload.message || JSON.stringify(payload), payload.status === 'error' ? 'error' : payload.status === 'ok' ? 'ok' : 'warn')
1470
- else if (payload.type === 'done') appendConsole('Done.', payload.success === false ? 'error' : 'ok')
1471
- else appendConsole(JSON.stringify(payload), '')
1472
- }
1530
+ await flush(decoder.decode(value, { stream: true }))
1473
1531
  }
1532
+ if (buffer) await flush(buffer, true)
1533
+ return events
1534
+ }
1535
+
1536
+ async function consumeSse(path, body, title) {
1537
+ await postMaybeSse(path, body, title)
1474
1538
  }
1475
1539
 
1476
1540
  function configureTool(toolId) {
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.69",
15
+ version: "2.1.70",
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",
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",
20
20
  prepublishOnly: "npm run build && npm test && node scripts/check-tarball-size.js"
21
21
  },
22
22
  keywords: [
@@ -9712,16 +9712,16 @@ var require_aionui_wrapper = __commonJS({
9712
9712
  d.addEventListener('click',function(){if(d.parentNode)d.parentNode.removeChild(d);sessionStorage.setItem(DISMISS_KEY,'1')});
9713
9713
  setTimeout(function(){if(d.parentNode)d.parentNode.removeChild(d);},30000);
9714
9714
  })();
9715
- // [v2.1.59] CLI upgrade panel \u2014 floating "\u{1F527} CLI \u5DE5\u5177" button bottom-left
9715
+ // [v2.1.70] Single CLI management panel \u2014 floating "\u{1F527} CLI \u7BA1\u7406" button bottom-left
9716
9716
  (function(){
9717
- var btn=el('div','position:fixed;bottom:16px;left:16px;z-index:99998;background:#2d3748;color:#fff;padding:8px 14px;border-radius:20px;font-size:12px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.2);user-select:none','\u{1F527} CLI \u5DE5\u5177');
9718
- btn.title='\u67E5\u770B/\u5347\u7EA7\u6240\u6709 CLI \u5DE5\u5177';
9717
+ var btn=el('div','position:fixed;bottom:16px;left:16px;z-index:99998;background:#2d3748;color:#fff;padding:8px 14px;border-radius:20px;font-size:12px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.2);user-select:none','\u{1F527} CLI \u7BA1\u7406');
9718
+ btn.title='\u7EDF\u4E00\u7BA1\u7406 CLI\u3001MCP \u4E0E VS Code Claude \u4EE3\u7406';
9719
9719
  document.body.appendChild(btn);
9720
9720
  var panel=null;
9721
9721
  btn.addEventListener('click',function(){
9722
9722
  if(panel){if(panel.parentNode)panel.parentNode.removeChild(panel);panel=null;return}
9723
9723
  panel=el('div','position:fixed;bottom:56px;left:16px;z-index:99998;background:#fff;color:#222;width:420px;max-height:70vh;overflow-y:auto;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,.25);padding:16px;font-size:13px',
9724
- '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px"><b>CLI \u5DE5\u5177\u7BA1\u7406</b><span id="hs-cli-close" style="cursor:pointer;font-size:16px;color:#999">\xD7</span></div>'+
9724
+ '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px"><b>CLI \u7BA1\u7406</b><span id="hs-cli-close" style="cursor:pointer;font-size:16px;color:#999">\xD7</span></div>'+
9725
9725
  '<div id="hs-cli-list" style="color:#666">\u52A0\u8F7D\u4E2D...</div>'+
9726
9726
  '<div id="hs-cli-log" style="margin-top:12px;font-size:11px;background:#1e1e1e;color:#0f0;padding:8px;border-radius:4px;max-height:200px;overflow-y:auto;display:none;font-family:monospace;white-space:pre-wrap"></div>');
9727
9727
  document.body.appendChild(panel);
@@ -9756,31 +9756,16 @@ var require_aionui_wrapper = __commonJS({
9756
9756
  if(allBtn)allBtn.addEventListener('click',function(){runAllUpgrade(allBtn)});
9757
9757
  }
9758
9758
  function runAction(act,id,b){
9759
- // [v2.1.63] launch returns plain JSON (not SSE) with { url, ... }
9760
9759
  if(act==='launch'){
9761
9760
  b.disabled=true;b.style.opacity='.5';b.textContent='\u542F\u52A8\u4E2D...';
9762
- logEl.style.display='block';logEl.textContent='\u6B63\u5728\u542F\u52A8 '+id+'...\\n';
9763
- fetch('/api/holysheep/tool/launch',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({toolId:id})})
9764
- .then(function(r){return r.json()})
9765
- .then(function(j){
9766
- b.disabled=false;b.style.opacity='1';
9767
- if(j&&j.url){
9768
- logEl.textContent+='\u5DF2\u542F\u52A8: '+j.url+'\\n';
9769
- b.textContent='\u2713 \u5DF2\u542F\u52A8';
9770
- try{ window.open(j.url,'_blank') }catch(e){}
9771
- }else if(j&&j.success===false){
9772
- logEl.textContent+='\u542F\u52A8\u5931\u8D25: '+(j.message||j.error||'unknown')+'\\n';
9773
- b.textContent='\u2717 \u5931\u8D25 (\u91CD\u8BD5)';
9774
- }else{
9775
- logEl.textContent+=JSON.stringify(j)+'\\n';
9776
- b.textContent='\u2713 \u5DF2\u542F\u52A8';
9777
- }
9778
- setTimeout(loadTools,500);
9779
- })
9780
- .catch(function(e){
9781
- b.disabled=false;b.style.opacity='1';b.textContent='\u2717 \u5931\u8D25 (\u91CD\u8BD5)';
9782
- logEl.textContent+='\u8BF7\u6C42\u5931\u8D25: '+e.message+'\\n';
9783
- });
9761
+ logEl.style.display='block';logEl.textContent='\u6B63\u5728\u542F\u52A8 '+id+'...
9762
+ ';
9763
+ postMaybeSse('/api/holysheep/tool/launch',{toolId:id},logEl,function(ok,j){
9764
+ b.disabled=false;b.style.opacity='1';
9765
+ if(j&&j.url){try{ window.open(j.url,'_blank') }catch(e){}}
9766
+ b.textContent=ok?'\u2713 \u5DF2\u542F\u52A8':'\u2717 \u5931\u8D25 (\u91CD\u8BD5)';
9767
+ setTimeout(loadTools,500);
9768
+ });
9784
9769
  return;
9785
9770
  }
9786
9771
  b.disabled=true;b.style.opacity='.5';b.textContent=act==='install'?'\u5B89\u88C5\u4E2D...':act==='upgrade'?'\u5347\u7EA7\u4E2D...':'\u914D\u7F6E\u4E2D...';
@@ -9799,31 +9784,56 @@ var require_aionui_wrapper = __commonJS({
9799
9784
  setTimeout(loadTools,500);
9800
9785
  });
9801
9786
  }
9802
- function consumeSse(url,body,logEl,done){
9787
+ function contentTypeOf(r){return String((r.headers&&r.headers.get&&r.headers.get('content-type'))||'').toLowerCase()}
9788
+ function appendSseEvent(ev,logEl){
9789
+ if(ev.type==='output'&&ev.text){logEl.textContent+=ev.text;logEl.scrollTop=logEl.scrollHeight}
9790
+ else if(ev.type==='progress'&&ev.message){logEl.textContent+='\u2192 '+ev.message+'
9791
+ ';logEl.scrollTop=logEl.scrollHeight}
9792
+ else if(ev.type==='tool'&&ev.status){logEl.textContent+='['+ev.name+'] '+ev.status+(ev.newVer?' v'+ev.newVer:'')+'
9793
+ '}
9794
+ else if(ev.type==='done'&&ev.success===false){logEl.textContent+='\u2717 '+(ev.message||ev.error||'\u5931\u8D25')+'
9795
+ '}
9796
+ }
9797
+ function consumeSseResponse(r,logEl,done){
9798
+ var reader=r.body.getReader();var dec=new TextDecoder();var buf='';var ok=true;
9799
+ function handlePart(p){
9800
+ var line=p.split('
9801
+ ').find(function(l){return l.startsWith('data:')});
9802
+ if(!line)return;
9803
+ try{var ev=JSON.parse(line.slice(5).trim());if(ev.type==='done'&&ev.success===false)ok=false;appendSseEvent(ev,logEl)}catch(e){logEl.textContent+=line.slice(5).trim()+'
9804
+ '}
9805
+ }
9806
+ function pump(){
9807
+ reader.read().then(function(s){
9808
+ if(s.done){if(buf)handlePart(buf);return done(ok)}
9809
+ buf+=dec.decode(s.value,{stream:true});
9810
+ var parts=buf.split('
9811
+
9812
+ ');buf=parts.pop();parts.forEach(handlePart);pump();
9813
+ }).catch(function(e){logEl.textContent+='
9814
+ [\u9519\u8BEF] '+e.message;done(false)})
9815
+ }
9816
+ pump();
9817
+ }
9818
+ function postMaybeSse(url,body,logEl,done){
9803
9819
  fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body||{})}).then(function(r){
9804
- var reader=r.body.getReader();var dec=new TextDecoder();var buf='';var ok=true;
9805
- function pump(){
9806
- reader.read().then(function(s){
9807
- if(s.done){return done(ok)}
9808
- buf+=dec.decode(s.value,{stream:true});
9809
- var parts=buf.split('\\n\\n');buf=parts.pop();
9810
- parts.forEach(function(p){
9811
- var line=p.split('\\n').find(function(l){return l.startsWith('data:')});
9812
- if(!line)return;
9813
- try{
9814
- var ev=JSON.parse(line.slice(5).trim());
9815
- if(ev.type==='output'&&ev.text){logEl.textContent+=ev.text;logEl.scrollTop=logEl.scrollHeight}
9816
- else if(ev.type==='progress'&&ev.message){logEl.textContent+='\u2192 '+ev.message+'\\n';logEl.scrollTop=logEl.scrollHeight}
9817
- else if(ev.type==='tool'&&ev.status){logEl.textContent+='['+ev.name+'] '+ev.status+(ev.newVer?' v'+ev.newVer:'')+'\\n'}
9818
- else if(ev.type==='done'){if(ev.success===false)ok=false}
9819
- }catch(e){}
9820
- });
9821
- pump();
9822
- }).catch(function(e){logEl.textContent+='\\n[\u9519\u8BEF] '+e.message;done(false)})
9823
- }
9824
- pump();
9825
- }).catch(function(e){logEl.textContent='\u8BF7\u6C42\u5931\u8D25: '+e.message;done(false)});
9820
+ var ct=contentTypeOf(r);
9821
+ if(ct.indexOf('text/event-stream')>=0)return consumeSseResponse(r,logEl,done);
9822
+ if(ct.indexOf('application/json')>=0)return r.json().then(function(j){
9823
+ if(j&&j.url)logEl.textContent+='\u5DF2\u542F\u52A8: '+j.url+'
9824
+ ';
9825
+ else if(j&&(j.ok===false||j.success===false))logEl.textContent+='\u5931\u8D25: '+(j.message||j.error||'unknown')+'
9826
+ ';
9827
+ else logEl.textContent+=JSON.stringify(j)+'
9828
+ ';
9829
+ done(!(j&&(j.ok===false||j.success===false)),j);
9830
+ });
9831
+ return r.text().then(function(t){logEl.textContent+=t+'
9832
+ ';done(r.ok)})
9833
+ }).catch(function(e){logEl.textContent+='\u8BF7\u6C42\u5931\u8D25: '+e.message+'
9834
+ ';done(false)});
9826
9835
  }
9836
+ function consumeSse(url,body,logEl,done){postMaybeSse(url,body,logEl,done)}
9827
9837
  function loadTools(){
9828
9838
  fetch('/api/holysheep/tools').then(function(r){return r.json()}).then(renderTools).catch(function(e){listEl.innerHTML='<i style="color:#dc2626">\u52A0\u8F7D\u5931\u8D25: '+e.message+'</i>'});
9829
9839
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@simonyea/holysheep-cli",
3
- "version": "2.1.69",
3
+ "version": "2.1.70",
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",
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",
8
8
  "prepublishOnly": "npm run build && npm test && node scripts/check-tarball-size.js"
9
9
  },
10
10
  "keywords": [