@simonyea/holysheep-cli 2.1.68 → 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.68",
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",
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
@@ -470,6 +470,27 @@ button { cursor: pointer; }
470
470
  color: var(--muted);
471
471
  line-height: 1.6;
472
472
  }
473
+ .tool-substatus {
474
+ margin-top: 12px;
475
+ border: 1px solid var(--line);
476
+ background: rgba(208, 110, 43, 0.08);
477
+ border-radius: 14px;
478
+ padding: 12px;
479
+ display: flex;
480
+ justify-content: space-between;
481
+ gap: 12px;
482
+ align-items: center;
483
+ }
484
+ .tool-substatus-title {
485
+ font-size: 13px;
486
+ font-weight: 700;
487
+ color: var(--text);
488
+ }
489
+ .tool-substatus-meta {
490
+ margin-top: 4px;
491
+ font-size: 12px;
492
+ color: var(--muted);
493
+ }
473
494
  .tool-actions {
474
495
  margin-top: 16px;
475
496
  display: flex;
@@ -563,7 +584,7 @@ button { cursor: pointer; }
563
584
  <button class="nav-item" data-route="dashboard"><span class="nav-dot"></span><span>Dashboard</span></button>
564
585
  <button class="nav-item" data-route="workspace"><span class="nav-dot"></span><span>Conversations</span></button>
565
586
  <button class="nav-item" data-route="tasks"><span class="nav-dot"></span><span>Scheduled Tasks</span></button>
566
- <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>
567
588
  <button class="nav-item" data-route="account"><span class="nav-dot"></span><span>HolySheep Account</span></button>
568
589
  </div>
569
590
  <div class="sidebar-card">
@@ -625,8 +646,8 @@ const pageMeta = {
625
646
  subtitle: 'Manage recurring prompts and maintain the default HolySheep API config directly below the task list.',
626
647
  },
627
648
  tools: {
628
- title: 'Tools & MCP',
629
- 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.',
630
651
  },
631
652
  account: {
632
653
  title: 'HolySheep Account',
@@ -658,16 +679,81 @@ function formatDate(value) {
658
679
  return date.toLocaleString()
659
680
  }
660
681
 
661
- function api(path, options) {
662
- 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')) {
663
693
  const payload = await response.json().catch(() => ({}))
664
- if (!response.ok) {
665
- const error = new Error(payload.error || payload.message || `HTTP ${response.status}`)
666
- error.payload = payload
667
- 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}`)
668
751
  }
669
752
  return payload
670
- })
753
+ }
754
+ if (!response.ok) throw new Error(parsed.text || `HTTP ${response.status}`)
755
+ if (parsed.text) appendConsole(parsed.text, '')
756
+ return parsed
671
757
  }
672
758
 
673
759
  async function loadBaseState() {
@@ -791,7 +877,7 @@ function renderDashboard() {
791
877
  </div>
792
878
  <div style="margin-top:16px;" class="inline-actions">
793
879
  <button class="btn primary" onclick="setRoute('tasks')">Open Scheduled Tasks</button>
794
- <button class="btn" onclick="setRoute('tools')">Manage CLI tools</button>
880
+ <button class="btn" onclick="setRoute('tools')">打开 CLI 管理</button>
795
881
  </div>
796
882
  </div>
797
883
  </section>
@@ -1033,12 +1119,12 @@ function renderTools() {
1033
1119
  <section class="panel">
1034
1120
  <div class="panel-header">
1035
1121
  <div>
1036
- <h2>Tools & MCP</h2>
1037
- <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>
1038
1124
  </div>
1039
1125
  </div>
1040
1126
  <div class="panel-body tool-grid">
1041
- ${tools.map((tool) => `
1127
+ ${tools.filter((tool) => tool.id !== 'claude-proxy').map((tool) => `
1042
1128
  <div class="tool-card">
1043
1129
  <div class="tool-head">
1044
1130
  <div>
@@ -1055,6 +1141,7 @@ function renderTools() {
1055
1141
  </div>
1056
1142
  </div>
1057
1143
  <div class="tool-hint">${esc(tool.hint || 'No additional hint')}</div>
1144
+ ${tool.id === 'claude-code' ? renderClaudeProxySubstatus(tool.vsCodeProxy) : ''}
1058
1145
  </div>
1059
1146
  `).join('')}
1060
1147
  </div>
@@ -1062,6 +1149,25 @@ function renderTools() {
1062
1149
  `
1063
1150
  }
1064
1151
 
1152
+ function renderClaudeProxySubstatus(proxy) {
1153
+ const running = !!proxy?.running
1154
+ const meta = running
1155
+ ? `127.0.0.1:${proxy.port || '?'} · PID ${proxy.pid || '?'}`
1156
+ : '未启动 · Windows 启动后需要完全退出 Code.exe 再重开 VS Code'
1157
+ return `
1158
+ <div class="tool-substatus">
1159
+ <div>
1160
+ <div class="tool-substatus-title">VS Code Claude 代理</div>
1161
+ <div class="tool-substatus-meta">${esc(meta)}</div>
1162
+ </div>
1163
+ <div class="inline-actions">
1164
+ <button class="btn ${running ? '' : 'primary'}" onclick="startClaudeProxy()">${running ? '重新校准' : '启动代理'}</button>
1165
+ ${running ? '<button class="btn danger" onclick="stopClaudeProxy()">停止</button>' : ''}
1166
+ </div>
1167
+ </div>
1168
+ `
1169
+ }
1170
+
1065
1171
  function renderAccount() {
1066
1172
  const status = state.status || {}
1067
1173
  const balance = state.balance || {}
@@ -1344,6 +1450,7 @@ function copyLoginKey() {
1344
1450
  }
1345
1451
 
1346
1452
  async function launchTool(toolId) {
1453
+ if (toolId === 'claude-proxy') return startClaudeProxy()
1347
1454
  const result = await api('tool/launch', {
1348
1455
  method: 'POST',
1349
1456
  headers: { 'Content-Type': 'application/json' },
@@ -1352,6 +1459,31 @@ async function launchTool(toolId) {
1352
1459
  if (result?.url) alert(`Opened: ${result.url}`)
1353
1460
  }
1354
1461
 
1462
+ async function startClaudeProxy() {
1463
+ try {
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')
1467
+ appendConsole('Windows 用户:请完全退出所有 Code.exe,再重新打开 VS Code。', 'warn')
1468
+ await loadBaseState()
1469
+ render()
1470
+ } catch (error) {
1471
+ appendConsole(error.message, 'error')
1472
+ }
1473
+ }
1474
+
1475
+ async function stopClaudeProxy() {
1476
+ try {
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')
1480
+ await loadBaseState()
1481
+ render()
1482
+ } catch (error) {
1483
+ appendConsole(error.message, 'error')
1484
+ }
1485
+ }
1486
+
1355
1487
  async function launchCurrentTool() {
1356
1488
  if (!state.currentConversation?.toolId) return
1357
1489
  return launchTool(state.currentConversation.toolId)
@@ -1376,32 +1508,33 @@ document.getElementById('console-close').onclick = () => {
1376
1508
  document.getElementById('console-drawer').classList.remove('open')
1377
1509
  }
1378
1510
 
1379
- async function consumeSse(path, body, title) {
1380
- openConsole(title)
1381
- const response = await fetch(`/api/${path}`, {
1382
- method: 'POST',
1383
- headers: { 'Content-Type': 'application/json' },
1384
- body: JSON.stringify(body),
1385
- })
1511
+ async function consumeSseResponse(response) {
1386
1512
  const reader = response.body.getReader()
1387
1513
  const decoder = new TextDecoder()
1514
+ const events = []
1388
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
+ }
1389
1527
  while (true) {
1390
1528
  const { value, done } = await reader.read()
1391
1529
  if (done) break
1392
- buffer += decoder.decode(value, { stream: true })
1393
- const chunks = buffer.split('\n\n')
1394
- buffer = chunks.pop()
1395
- for (const chunk of chunks) {
1396
- const line = chunk.split('\n').find((entry) => entry.startsWith('data:'))
1397
- if (!line) continue
1398
- const payload = JSON.parse(line.slice(5).trim())
1399
- if (payload.type === 'output') appendConsole(payload.text, '')
1400
- else if (payload.type === 'progress') appendConsole(payload.message || JSON.stringify(payload), payload.status === 'error' ? 'error' : payload.status === 'ok' ? 'ok' : 'warn')
1401
- else if (payload.type === 'done') appendConsole('Done.', payload.success === false ? 'error' : 'ok')
1402
- else appendConsole(JSON.stringify(payload), '')
1403
- }
1530
+ await flush(decoder.decode(value, { stream: true }))
1404
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)
1405
1538
  }
1406
1539
 
1407
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.68",
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",
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: [
@@ -7685,7 +7685,7 @@ var require_server = __commonJS({
7685
7685
  async function buildToolSummary(tool) {
7686
7686
  var _a, _b;
7687
7687
  const installed = await detectToolInstalled(tool);
7688
- return {
7688
+ const summary = {
7689
7689
  id: tool.id,
7690
7690
  name: tool.name,
7691
7691
  installed,
@@ -7698,6 +7698,18 @@ var require_server = __commonJS({
7698
7698
  canUpgrade: !!UPGRADABLE_TOOLS.find((item) => item.id === tool.id),
7699
7699
  npmPkg: ((_b = UPGRADABLE_TOOLS.find((item) => item.id === tool.id)) == null ? void 0 : _b.npmPkg) || null
7700
7700
  };
7701
+ if (tool.id === "claude-code") {
7702
+ const proxyState = isClaudeProxyRunning();
7703
+ summary.vsCodeProxy = {
7704
+ running: proxyState.running,
7705
+ configured: proxyState.running,
7706
+ port: proxyState.port || null,
7707
+ pid: proxyState.pid || null,
7708
+ launchCmd: "hs claude-proxy --daemon",
7709
+ hint: proxyState.running ? `VS Code \u4EE3\u7406\u8FD0\u884C\u4E2D\uFF1A127.0.0.1:${proxyState.port}` : "VS Code Claude Code \u9700\u8981\u542F\u52A8\u672C\u5730\u4EE3\u7406\uFF1B\u542F\u52A8\u540E\u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u5F00 VS Code\u3002"
7710
+ };
7711
+ }
7712
+ return summary;
7701
7713
  }
7702
7714
  __name(buildToolSummary, "buildToolSummary");
7703
7715
  function json(res, data, status = 200) {
@@ -7943,23 +7955,85 @@ var require_server = __commonJS({
7943
7955
  __name(isClaudeProxyRunning, "isClaudeProxyRunning");
7944
7956
  async function handleTools(_req, res) {
7945
7957
  const tools = await Promise.all(TOOLS.map((tool) => buildToolSummary(tool)));
7946
- const proxyState = isClaudeProxyRunning();
7947
- tools.push({
7948
- id: "claude-proxy",
7949
- name: "Claude \u4EE3\u7406\uFF08VS Code\uFF09",
7950
- installed: true,
7951
- configured: proxyState.running,
7952
- version: proxyState.running ? `\u7AEF\u53E3 ${proxyState.port}` : null,
7953
- installCmd: null,
7954
- hint: "\u542F\u52A8\u540E VS Code Claude \u6269\u5C55\u5373\u53EF\u901A\u8FC7 HolySheep \u4F7F\u7528",
7955
- launchCmd: "hs claude-proxy",
7956
- canAutoInstall: false,
7957
- canUpgrade: false,
7958
- isProxy: true
7959
- });
7960
7958
  json(res, tools);
7961
7959
  }
7962
7960
  __name(handleTools, "handleTools");
7961
+ function isClaudeProxyToolId(toolId) {
7962
+ return toolId === "claude-proxy";
7963
+ }
7964
+ __name(isClaudeProxyToolId, "isClaudeProxyToolId");
7965
+ function emitClaudeProxyUnsupportedSse(res, action) {
7966
+ sseStart(res);
7967
+ sseEmit(res, {
7968
+ type: "progress",
7969
+ status: "error",
7970
+ message: `Claude Code / VS Code \u4EE3\u7406\u4E0D\u652F\u6301 ${action}\uFF1B\u8BF7\u4F7F\u7528\u201C\u542F\u52A8 VS Code \u4EE3\u7406\u201D\u6216\u8FD0\u884C hs claude-proxy --daemon\u3002`
7971
+ });
7972
+ sseEmit(res, { type: "done", success: false, message: "unsupported-claude-proxy-action" });
7973
+ res.end();
7974
+ }
7975
+ __name(emitClaudeProxyUnsupportedSse, "emitClaudeProxyUnsupportedSse");
7976
+ async function handleClaudeProxyToolStart(res) {
7977
+ sseStart(res);
7978
+ const state = isClaudeProxyRunning();
7979
+ if (state.running) {
7980
+ sseEmit(res, { type: "progress", status: "ok", message: `VS Code \u4EE3\u7406\u5DF2\u5728\u8FD0\u884C\uFF1A127.0.0.1:${state.port} (PID ${state.pid})` });
7981
+ sseEmit(res, { type: "done", success: true, port: state.port, pid: state.pid });
7982
+ return res.end();
7983
+ }
7984
+ try {
7985
+ sseEmit(res, { type: "progress", message: "\u6B63\u5728\u542F\u52A8 VS Code \u4EE3\u7406..." });
7986
+ const claudeProxy = require_claude_proxy();
7987
+ await claudeProxy(["--daemon"]);
7988
+ await new Promise((r) => setTimeout(r, 1200));
7989
+ const newState = isClaudeProxyRunning();
7990
+ if (!newState.running) {
7991
+ throw new Error("claude-proxy daemon \u542F\u52A8\u540E\u672A\u5199\u5165\u5065\u5EB7 PID\uFF1B\u8BF7\u67E5\u770B ~/.holysheep/claude-proxy.log");
7992
+ }
7993
+ sseEmit(res, { type: "progress", status: "ok", message: `VS Code \u4EE3\u7406\u5DF2\u542F\u52A8\uFF1A127.0.0.1:${newState.port} (PID ${newState.pid})` });
7994
+ sseEmit(res, { type: "progress", message: "Windows \u7528\u6237\uFF1A\u8BF7\u5B8C\u5168\u9000\u51FA\u6240\u6709 Code.exe\uFF0C\u518D\u91CD\u65B0\u6253\u5F00 VS Code\u3002" });
7995
+ sseEmit(res, { type: "done", success: true, port: newState.port, pid: newState.pid });
7996
+ res.end();
7997
+ } catch (e) {
7998
+ sseEmit(res, { type: "progress", status: "error", message: e && e.message ? e.message : String(e) });
7999
+ sseEmit(res, { type: "done", success: false, error: e && e.message ? e.message : String(e) });
8000
+ res.end();
8001
+ }
8002
+ }
8003
+ __name(handleClaudeProxyToolStart, "handleClaudeProxyToolStart");
8004
+ async function handleClaudeProxyToolStop(res) {
8005
+ sseStart(res);
8006
+ try {
8007
+ const claudeProxy = require_claude_proxy();
8008
+ await claudeProxy(["--stop"]);
8009
+ sseEmit(res, { type: "progress", status: "ok", message: "VS Code \u4EE3\u7406\u5DF2\u505C\u6B62" });
8010
+ sseEmit(res, { type: "done", success: true });
8011
+ res.end();
8012
+ } catch (e) {
8013
+ sseEmit(res, { type: "progress", status: "error", message: e && e.message ? e.message : String(e) });
8014
+ sseEmit(res, { type: "done", success: false, error: e && e.message ? e.message : String(e) });
8015
+ res.end();
8016
+ }
8017
+ }
8018
+ __name(handleClaudeProxyToolStop, "handleClaudeProxyToolStop");
8019
+ async function handleClaudeProxyVirtualToolAction(req, res, action) {
8020
+ const body = await parseBody(req);
8021
+ if (!isClaudeProxyToolId(body.toolId)) return false;
8022
+ if (action === "launch" || action === "configure" || action === "install") {
8023
+ await handleClaudeProxyToolStart(res);
8024
+ return true;
8025
+ }
8026
+ if (action === "reset") {
8027
+ await handleClaudeProxyToolStop(res);
8028
+ return true;
8029
+ }
8030
+ if (action === "upgrade" || action === "rollback") {
8031
+ emitClaudeProxyUnsupportedSse(res, "\u5347\u7EA7/\u56DE\u9000");
8032
+ return true;
8033
+ }
8034
+ return false;
8035
+ }
8036
+ __name(handleClaudeProxyVirtualToolAction, "handleClaudeProxyVirtualToolAction");
7963
8037
  async function handleSetup(req, res) {
7964
8038
  const body = await parseBody(req);
7965
8039
  const { apiKey, models, toolIds, autoInstall } = body;
@@ -8135,6 +8209,7 @@ var require_server = __commonJS({
8135
8209
  }
8136
8210
  __name(handleUpgrade, "handleUpgrade");
8137
8211
  async function handleToolInstall(req, res) {
8212
+ if (await handleClaudeProxyVirtualToolAction(req, res, "install")) return;
8138
8213
  const body = await parseBody(req);
8139
8214
  const { toolId } = body;
8140
8215
  if (!toolId || !AUTO_INSTALL[toolId]) {
@@ -8293,6 +8368,7 @@ ${installCmd}
8293
8368
  }
8294
8369
  __name(handleToolInstall, "handleToolInstall");
8295
8370
  async function handleToolConfigure(req, res) {
8371
+ if (await handleClaudeProxyVirtualToolAction(req, res, "configure")) return;
8296
8372
  const body = await parseBody(req);
8297
8373
  const { toolId } = body;
8298
8374
  const apiKey = getApiKey();
@@ -8352,6 +8428,7 @@ ${installCmd}
8352
8428
  __name(handleToolConfigure, "handleToolConfigure");
8353
8429
  async function handleToolUpgrade(req, res) {
8354
8430
  var _a;
8431
+ if (await handleClaudeProxyVirtualToolAction(req, res, "upgrade")) return;
8355
8432
  const body = await parseBody(req);
8356
8433
  const { toolId } = body;
8357
8434
  const entry = UPGRADABLE_TOOLS.find((t) => t.id === toolId);
@@ -8459,6 +8536,7 @@ ${installCmd}
8459
8536
  __name(handleToolUpgrade, "handleToolUpgrade");
8460
8537
  async function handleToolRollback(req, res) {
8461
8538
  var _a;
8539
+ if (await handleClaudeProxyVirtualToolAction(req, res, "rollback")) return;
8462
8540
  const body = await parseBody(req);
8463
8541
  const { toolId } = body;
8464
8542
  const entry = UPGRADABLE_TOOLS.find((t) => t.id === toolId);
@@ -8613,6 +8691,7 @@ ${installCmd}
8613
8691
  }
8614
8692
  __name(handleToolRollback, "handleToolRollback");
8615
8693
  async function handleToolReset(req, res) {
8694
+ if (await handleClaudeProxyVirtualToolAction(req, res, "reset")) return;
8616
8695
  const body = await parseBody(req);
8617
8696
  const { toolId } = body;
8618
8697
  const tool = TOOLS.find((t) => t.id === toolId);
@@ -8627,6 +8706,7 @@ ${installCmd}
8627
8706
  __name(handleToolReset, "handleToolReset");
8628
8707
  async function handleToolLaunch(req, res) {
8629
8708
  var _a, _b;
8709
+ if (await handleClaudeProxyVirtualToolAction(req, res, "launch")) return;
8630
8710
  const body = await parseBody(req);
8631
8711
  const { toolId } = body;
8632
8712
  const tool = TOOLS.find((t) => t.id === toolId);
@@ -8676,7 +8756,10 @@ ${installCmd}
8676
8756
  await claudeProxy(["--daemon"]);
8677
8757
  await new Promise((r) => setTimeout(r, 2e3));
8678
8758
  const newState = isClaudeProxyRunning();
8679
- json(res, { ok: newState.running, port: newState.port, pid: newState.pid });
8759
+ if (!newState.running) {
8760
+ return json(res, { ok: false, error: "claude-proxy daemon \u542F\u52A8\u540E\u672A\u5199\u5165\u5065\u5EB7 PID\uFF1B\u8BF7\u67E5\u770B ~/.holysheep/claude-proxy.log" }, 500);
8761
+ }
8762
+ json(res, { ok: true, port: newState.port, pid: newState.pid });
8680
8763
  } catch (e) {
8681
8764
  json(res, { ok: false, error: e.message }, 500);
8682
8765
  }
@@ -9091,6 +9174,8 @@ ${installCmd}
9091
9174
  handleToolReset,
9092
9175
  handleToolLaunch,
9093
9176
  handleToolUpgrade,
9177
+ handleClaudeProxyStart,
9178
+ handleClaudeProxyStop,
9094
9179
  handleUpgrade,
9095
9180
  handleBalance,
9096
9181
  handleDoctor,
@@ -9627,16 +9712,16 @@ var require_aionui_wrapper = __commonJS({
9627
9712
  d.addEventListener('click',function(){if(d.parentNode)d.parentNode.removeChild(d);sessionStorage.setItem(DISMISS_KEY,'1')});
9628
9713
  setTimeout(function(){if(d.parentNode)d.parentNode.removeChild(d);},30000);
9629
9714
  })();
9630
- // [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
9631
9716
  (function(){
9632
- 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');
9633
- 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';
9634
9719
  document.body.appendChild(btn);
9635
9720
  var panel=null;
9636
9721
  btn.addEventListener('click',function(){
9637
9722
  if(panel){if(panel.parentNode)panel.parentNode.removeChild(panel);panel=null;return}
9638
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',
9639
- '<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>'+
9640
9725
  '<div id="hs-cli-list" style="color:#666">\u52A0\u8F7D\u4E2D...</div>'+
9641
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>');
9642
9727
  document.body.appendChild(panel);
@@ -9656,7 +9741,13 @@ var require_aionui_wrapper = __commonJS({
9656
9741
  if(t.installed&&!t.configured)btns+='<button data-act="configure" data-id="'+t.id+'" style="'+btnSty+';background:#8b5cf6">\u914D\u7F6E</button>';
9657
9742
  // [v2.1.63] \u542F\u52A8 button \u2014 installed + has launchCmd (covers OpenClaw etc.)
9658
9743
  if(t.installed&&t.launchCmd)btns+='<button data-act="launch" data-id="'+t.id+'" style="'+btnSty+';background:#f59e0b">\u542F\u52A8</button>';
9659
- return '<div style="padding:8px 0;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center"><div><b>'+t.name+'</b> '+statusBadge+' '+verText+'</div><div>'+btns+'</div></div>';
9744
+ var proxy='';
9745
+ if(t.id==='claude-code'&&t.vsCodeProxy){
9746
+ var p=t.vsCodeProxy;
9747
+ var pText=p.running?('\u8FD0\u884C\u4E2D 127.0.0.1:'+p.port+' \xB7 PID '+p.pid):'\u672A\u542F\u52A8\uFF08Windows \u542F\u52A8\u540E\u9700\u5B8C\u5168\u9000\u51FA Code.exe \u518D\u91CD\u5F00 VS Code\uFF09';
9748
+ proxy='<div style="margin-top:6px;padding:6px 8px;background:#fff7ed;border:1px solid #fed7aa;border-radius:6px;font-size:11px;color:#9a3412">VS Code \u4EE3\u7406\uFF1A'+pText+' <button data-act="launch" data-id="claude-proxy" style="'+btnSty+';background:#f59e0b">'+(p.running?'\u91CD\u65B0\u6821\u51C6':'\u542F\u52A8\u4EE3\u7406')+'</button>'+(p.running?' <button data-act="reset" data-id="claude-proxy" style="'+btnSty+';background:#dc2626">\u505C\u6B62</button>':'')+'</div>';
9749
+ }
9750
+ return '<div style="padding:8px 0;border-bottom:1px solid #eee"><div style="display:flex;justify-content:space-between;align-items:center"><div><b>'+t.name+'</b> '+statusBadge+' '+verText+'</div><div>'+btns+'</div></div>'+proxy+'</div>';
9660
9751
  }).join('')+'<div style="padding-top:10px"><button id="hs-cli-upgrade-all" style="width:100%;padding:8px;background:#16a34a;color:#fff;border:0;border-radius:4px;cursor:pointer;font-weight:600">\u4E00\u952E\u5347\u7EA7\u5168\u90E8\u5DF2\u5B89\u88C5\u7684 CLI</button></div>';
9661
9752
  Array.prototype.forEach.call(panel.querySelectorAll('button[data-act]'),function(b){
9662
9753
  b.addEventListener('click',function(){runAction(b.dataset.act,b.dataset.id,b)});
@@ -9665,36 +9756,21 @@ var require_aionui_wrapper = __commonJS({
9665
9756
  if(allBtn)allBtn.addEventListener('click',function(){runAllUpgrade(allBtn)});
9666
9757
  }
9667
9758
  function runAction(act,id,b){
9668
- // [v2.1.63] launch returns plain JSON (not SSE) with { url, ... }
9669
9759
  if(act==='launch'){
9670
9760
  b.disabled=true;b.style.opacity='.5';b.textContent='\u542F\u52A8\u4E2D...';
9671
- logEl.style.display='block';logEl.textContent='\u6B63\u5728\u542F\u52A8 '+id+'...\\n';
9672
- fetch('/api/holysheep/tool/launch',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({toolId:id})})
9673
- .then(function(r){return r.json()})
9674
- .then(function(j){
9675
- b.disabled=false;b.style.opacity='1';
9676
- if(j&&j.url){
9677
- logEl.textContent+='\u5DF2\u542F\u52A8: '+j.url+'\\n';
9678
- b.textContent='\u2713 \u5DF2\u542F\u52A8';
9679
- try{ window.open(j.url,'_blank') }catch(e){}
9680
- }else if(j&&j.success===false){
9681
- logEl.textContent+='\u542F\u52A8\u5931\u8D25: '+(j.message||j.error||'unknown')+'\\n';
9682
- b.textContent='\u2717 \u5931\u8D25 (\u91CD\u8BD5)';
9683
- }else{
9684
- logEl.textContent+=JSON.stringify(j)+'\\n';
9685
- b.textContent='\u2713 \u5DF2\u542F\u52A8';
9686
- }
9687
- setTimeout(loadTools,500);
9688
- })
9689
- .catch(function(e){
9690
- b.disabled=false;b.style.opacity='1';b.textContent='\u2717 \u5931\u8D25 (\u91CD\u8BD5)';
9691
- logEl.textContent+='\u8BF7\u6C42\u5931\u8D25: '+e.message+'\\n';
9692
- });
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
+ });
9693
9769
  return;
9694
9770
  }
9695
9771
  b.disabled=true;b.style.opacity='.5';b.textContent=act==='install'?'\u5B89\u88C5\u4E2D...':act==='upgrade'?'\u5347\u7EA7\u4E2D...':'\u914D\u7F6E\u4E2D...';
9696
9772
  logEl.style.display='block';logEl.textContent='';
9697
- var path=act==='install'?'tool/install':act==='upgrade'?'tool/upgrade':'tool/configure';
9773
+ var path=act==='install'?'tool/install':act==='upgrade'?'tool/upgrade':act==='reset'?'tool/reset':'tool/configure';
9698
9774
  consumeSse('/api/holysheep/'+path,{toolId:id},logEl,function(ok){
9699
9775
  b.disabled=false;b.style.opacity='1';b.textContent=ok?'\u2713 \u5B8C\u6210':'\u2717 \u5931\u8D25 (\u91CD\u8BD5)';
9700
9776
  setTimeout(loadTools,500);
@@ -9708,31 +9784,56 @@ var require_aionui_wrapper = __commonJS({
9708
9784
  setTimeout(loadTools,500);
9709
9785
  });
9710
9786
  }
9711
- 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){
9712
9819
  fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body||{})}).then(function(r){
9713
- var reader=r.body.getReader();var dec=new TextDecoder();var buf='';var ok=true;
9714
- function pump(){
9715
- reader.read().then(function(s){
9716
- if(s.done){return done(ok)}
9717
- buf+=dec.decode(s.value,{stream:true});
9718
- var parts=buf.split('\\n\\n');buf=parts.pop();
9719
- parts.forEach(function(p){
9720
- var line=p.split('\\n').find(function(l){return l.startsWith('data:')});
9721
- if(!line)return;
9722
- try{
9723
- var ev=JSON.parse(line.slice(5).trim());
9724
- if(ev.type==='output'&&ev.text){logEl.textContent+=ev.text;logEl.scrollTop=logEl.scrollHeight}
9725
- else if(ev.type==='progress'&&ev.message){logEl.textContent+='\u2192 '+ev.message+'\\n';logEl.scrollTop=logEl.scrollHeight}
9726
- else if(ev.type==='tool'&&ev.status){logEl.textContent+='['+ev.name+'] '+ev.status+(ev.newVer?' v'+ev.newVer:'')+'\\n'}
9727
- else if(ev.type==='done'){if(ev.success===false)ok=false}
9728
- }catch(e){}
9729
- });
9730
- pump();
9731
- }).catch(function(e){logEl.textContent+='\\n[\u9519\u8BEF] '+e.message;done(false)})
9732
- }
9733
- pump();
9734
- }).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)});
9735
9835
  }
9836
+ function consumeSse(url,body,logEl,done){postMaybeSse(url,body,logEl,done)}
9736
9837
  function loadTools(){
9737
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>'});
9738
9839
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@simonyea/holysheep-cli",
3
- "version": "2.1.68",
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",
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": [