@agentguardx/helper 0.3.89 → 0.3.91

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.
Files changed (2) hide show
  1. package/lib/wizard.cjs +24 -9
  2. package/package.json +7 -7
package/lib/wizard.cjs CHANGED
@@ -45,6 +45,9 @@ async function question(ask, label, masked = false) {
45
45
  }
46
46
  async function choose(ask, label, options, multiple = false) {
47
47
  let cursor = 0, selected = new Set();
48
+ // 上一步输入残留的回车(例如粘贴地址时末尾带的换行)会在菜单刚出现时立刻到达,
49
+ // 不能把它当成对默认项的选择;菜单显示后短时间内的回车一律忽略。
50
+ const shownAt = Date.now();
48
51
  const render = first => {
49
52
  if (!first) process.stdout.write(`\x1b[${options.length}A`);
50
53
  options.forEach((option, index) => process.stdout.write(`\x1b[2K\r${cursor === index ? '›' : ' '} ${multiple ? `[${selected.has(index) ? '✓' : ' '}] ` : ''}${option.label}\n`));
@@ -53,6 +56,7 @@ async function choose(ask, label, options, multiple = false) {
53
56
  if (key.name === 'up') cursor = (cursor + options.length - 1) % options.length;
54
57
  else if (key.name === 'down') cursor = (cursor + 1) % options.length;
55
58
  else if (multiple && key.name === 'space') selected.has(cursor) ? selected.delete(cursor) : selected.add(cursor);
59
+ else if ((key.name === 'return' || key.name === 'enter') && Date.now() - shownAt < 300) return;
56
60
  else if (key.name === 'return' || key.name === 'enter') { done(multiple ? [...selected].sort().map(i => options[i].value) : options[cursor].value); return; }
57
61
  render(false);
58
62
  });
@@ -87,19 +91,22 @@ function config(server) {
87
91
  request.on('timeout', () => request.destroy()); request.on('error', () => reject(new Error('server_config_unavailable')));
88
92
  });
89
93
  }
90
- function detectedTools() {
94
+ function supportedTools() {
91
95
  const home = os.homedir(), windows = process.platform === 'win32';
92
96
  const command = name => (process.env.PATH || '').split(path.delimiter).some(dir => (windows ? ['', '.exe', '.cmd', '.bat'] : ['']).some(ext => {
93
97
  try { const file = path.join(dir, name + ext); return fs.statSync(file).isFile() && (windows || (fs.accessSync(file, fs.constants.X_OK), true)); } catch { return false; }
94
98
  }));
95
99
  const apps = (mac, win) => process.platform === 'darwin' ? ['/Applications', path.join(home, 'Applications')].some(dir => fs.existsSync(path.join(dir, mac))) : windows && !!process.env.LOCALAPPDATA && fs.existsSync(path.join(process.env.LOCALAPPDATA, win));
96
100
  return [
97
- { label: 'OpenCode(模型与远程 MCP)', value: 'opencode', present: command('opencode') },
98
- { label: 'OpenClaw(模型)', value: 'openclaw', present: command('openclaw') },
99
- { label: 'Cursor(远程 MCP)', value: 'cursor', present: command('cursor') || apps('Cursor.app', 'Programs/Cursor/Cursor.exe') },
100
- { label: 'Claude Desktop(MCP 桥接)', value: 'claude-desktop', present: command('claude-desktop') || apps('Claude.app', 'AnthropicClaude/Claude.exe') },
101
- { label: 'Cherry Studio(手动填写说明)', value: 'cherry-studio', present: apps('Cherry Studio.app', 'Programs/Cherry Studio/Cherry Studio.exe') },
102
- ].filter(item => item.present);
101
+ { label: 'OpenCode(模型与远程 MCP)', value: 'opencode', present: command('opencode'), install: 'npm i -g opencode-ai' },
102
+ { label: 'OpenClaw(模型)', value: 'openclaw', present: command('openclaw'), install: '按 OpenClaw 官方说明安装' },
103
+ { label: 'Cursor(远程 MCP)', value: 'cursor', present: command('cursor') || apps('Cursor.app', 'Programs/Cursor/Cursor.exe'), install: '从 cursor.com 下载安装' },
104
+ { label: 'Claude Desktop(MCP 桥接)', value: 'claude-desktop', present: command('claude-desktop') || apps('Claude.app', 'AnthropicClaude/Claude.exe'), install: '从 claude.ai/download 下载安装' },
105
+ { label: 'Cherry Studio(手动填写说明)', value: 'cherry-studio', present: apps('Cherry Studio.app', 'Programs/Cherry Studio/Cherry Studio.exe'), install: '从 cherry-ai.com 下载安装' },
106
+ ];
107
+ }
108
+ function detectedTools() {
109
+ return supportedTools().filter(item => item.present);
103
110
  }
104
111
  async function currentServer(runtime, prefix, flagServer) {
105
112
  // Priority --server > AGENTGUARD_SERVER > saved configuration is resolved by the Go core.
@@ -142,7 +149,10 @@ async function init(runtime, args) {
142
149
  const choices = [{ label: '浏览器网页授权登录(以控制台当前登录的人授权)', value: 'browser' }, { label: '粘贴个人访问令牌', value: 'token' }];
143
150
  let httpFlags = [];
144
151
  if (new URL(server).protocol === 'http:') {
145
- const consent = await queryConsent(runtime, server, prefix);
152
+ const loopback = ['127.0.0.1', 'localhost', '[::1]'].includes(new URL(server).hostname);
153
+ const consent = loopback ? { acknowledged: true } : await queryConsent(runtime, server, prefix);
154
+ // 本机回环地址不经过网络,没有中间人可读取,不再询问明文风险。
155
+ if (loopback) process.stdout.write('本机地址,不经过网络传输,无需明文风险确认。\n');
146
156
  if (!consent.acknowledged) {
147
157
  const answer = await choose(ask, 'HTTP 会以明文传输登录信息、个人令牌和业务内容,网络中间人可能读取或篡改。是否确认对该服务器继续?', [
148
158
  { label: '取消,不发送登录信息或令牌', value: false },
@@ -187,7 +197,12 @@ async function init(runtime, args) {
187
197
  } else code = await run(runtime, [...prefix, 'login', '--server', server, ...httpFlags, '--defer-notice'], '');
188
198
  if (code) { process.exitCode = code; return; }
189
199
  const tools = detectedTools();
190
- if (!tools.length) process.stdout.write('未检测到本批支持的工具;安装工具后可运行 agentguard connect <工具名>。\n');
200
+ const missing = supportedTools().filter(item => !item.present);
201
+ if (missing.length) {
202
+ process.stdout.write('以下支持的工具在本机未检测到,安装后运行 npx @agentguardx/helper connect <工具名> 接入:\n');
203
+ for (const item of missing) process.stdout.write(` - ${item.label}:${item.install}(工具名 ${item.value})\n`);
204
+ }
205
+ if (!tools.length) process.stdout.write('本机暂未检测到支持的工具。\n');
191
206
  else {
192
207
  const selected = await choose(ask, '选择要接入的已检测工具', tools, true);
193
208
  for (const tool of selected) { code = await run(runtime, [...prefix, 'connect', tool], ''); if (code) { process.exitCode = code; return; } }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentguardx/helper",
3
- "version": "0.3.89",
3
+ "version": "0.3.91",
4
4
  "description": "AgentGuard \u5458\u5de5\u63a5\u5165\u52a9\u624b",
5
5
  "license": "UNLICENSED",
6
6
  "engines": {
@@ -19,12 +19,12 @@
19
19
  "preuninstall": "node lib/preuninstall.cjs"
20
20
  },
21
21
  "optionalDependencies": {
22
- "@agentguardx/helper-darwin-x64": "0.3.89",
23
- "@agentguardx/helper-darwin-arm64": "0.3.89",
24
- "@agentguardx/helper-linux-x64": "0.3.89",
25
- "@agentguardx/helper-linux-arm64": "0.3.89",
26
- "@agentguardx/helper-win32-x64": "0.3.89",
27
- "@agentguardx/helper-win32-arm64": "0.3.89"
22
+ "@agentguardx/helper-darwin-x64": "0.3.91",
23
+ "@agentguardx/helper-darwin-arm64": "0.3.91",
24
+ "@agentguardx/helper-linux-x64": "0.3.91",
25
+ "@agentguardx/helper-linux-arm64": "0.3.91",
26
+ "@agentguardx/helper-win32-x64": "0.3.91",
27
+ "@agentguardx/helper-win32-arm64": "0.3.91"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public"