@agentguardx/helper 0.3.99 → 0.3.100

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.
package/README.md CHANGED
@@ -8,7 +8,7 @@ npm i -g @agentguardx/helper
8
8
  agentguard init --server https://企业服务器域名
9
9
  ```
10
10
 
11
- 公共 npmjs 尚未发布;可发布到企业私有仓库后用 `npx --registry <仓库地址> @agentguardx/helper`(见 docs/client/B13_INSTALLATION.md 的发布步骤),或使用管理员分发的离线 tgz。离线安装时同时安装主包和本机对应的平台包;全局命令仍为 `agentguard`。包不固定企业服务器地址;可交互填写,或使用 `--server` / `AGENTGUARD_SERVER`。支持 HTTP/HTTPS;HTTP 每个新服务器先明确确认明文风险,确认前不发送令牌。
11
+ 企业私有仓库使用 `npx --registry <仓库地址> @agentguardx/helper`(见 docs/client/B13_INSTALLATION.md 的发布步骤),也可使用管理员分发的离线 tgz。离线安装时同时安装主包和本机对应的平台包;全局命令仍为 `agentguard`。包不固定企业服务器地址;可交互填写,或使用 `--server` / `AGENTGUARD_SERVER`。支持 HTTP/HTTPS;HTTP 每个新服务器先明确确认明文风险,确认前不发送令牌。本版包含 WorkBuddy Hook 向导安装、独立配置核验与退出清理;公开发布状态以 npm registry 为准。
12
12
 
13
13
  npx 即用不会创建全局 `agentguard` 命令;后续使用 `npx @agentguardx/helper launch opencode`(OpenCode 已原生接入时也可直接运行 `opencode`)、`npx @agentguardx/helper uninstall`,或全局安装后使用短命令。
14
14
 
@@ -18,7 +18,21 @@ npx 即用不会创建全局 `agentguard` 命令;后续使用 `npx @agentguard
18
18
 
19
19
  企业对话正文留存不要求员工告知或确认,向导不展示告知步骤、不提交已读;正常对话不会因告知流程被阻断。留存开启仍须独立双审,正文访问按当前角色与服务端审批结果执行,平台管理员可审核本人申请;正式环境默认关闭。
20
20
 
21
- 支持入口为 OpenCode、WorkBuddy(自定义模型写入 `~/.workbuddy/models.json`、远程 MCP 写入 `mcp.json`,直接打开 WorkBuddy 并选“AgentGuard / …”模型即可;只有选用该模型的对话和 agentguard 连接器的调用经过 AgentGuard,WorkBuddy 自带模型、连接器与本机工具不经过)、OpenClaw、Cursor 远程 MCP 及 Cherry Studio 手动说明(Claude Desktop 向导不再提供,2026-10-23 用户决定);厂商原生实测范围以企业兼容记录为准。Codex 暂缓,Gemini、Claude Code、Claude Desktop 不接入。B15 为 OpenCode 同时注册本机安全 stdio MCP,并写入关闭自带本机工具的配置;仅经 ag_* 入口的调用由服务端逐次授权、执行上报和输出检查。其他厂商未验证关闭原生工具时,其自带本机终端、文件操作不受该 MCP 管控。客户端可被员工修改的边界不等同操作系统强制隔离。
21
+ 支持入口为 OpenCode、WorkBuddy(自定义模型写入 `~/.workbuddy/models.json`、远程 MCP 写入 `mcp.json`,本机安全 MCP 为 `agentguard_local`;对话需在应用内选“AgentGuard / …”模型)、OpenClaw、Cursor 远程 MCP 及 Cherry Studio 手动说明(Claude Desktop 向导不再提供,2026-10-23 用户决定);厂商原生实测范围以企业兼容记录为准。Codex 暂缓,Gemini、Claude Code、Claude Desktop 不接入。B15 为 OpenCode 同时注册本机安全 stdio MCP,并写入关闭自带本机工具的配置;仅经 ag_* 入口的调用由服务端逐次授权、执行上报和输出检查。其他厂商未验证关闭原生工具时,其自带本机终端、文件操作不受该 MCP 管控。客户端可被员工修改的边界不等同操作系统强制隔离。
22
+
23
+ WorkBuddy 向导会在模型/MCP 接入成功后,增量安装 `settings.json` 中的 PreToolUse、PostToolUse、PostToolUseFailure Hook,再独立读回核验;`WORKBUDDY_CONFIG_DIR` 可指定目录。保留已有模型、MCP、其他 Hook 和未知设置。安装失败、配置被修改或状态无法核验时,模型/MCP 接入保留,向导显示 Hook 未完成并以非零状态结束,不自动重试或覆盖。Windows 当前不支持 Hook 安装,向导单独提示未完成;macOS 已有隔离验证,Linux 尚未做 WorkBuddy 宿主实机验证。
24
+
25
+ 管理员须先部署对应的 WorkBuddy Hook 网关接口、工具登记与规则,并为其他 MCP 配置精确工具映射;未知工具会拒绝。Hook 配置核验不代表宿主已经加载,也不代表工具调用已通过真实验收;需另做允许/拒绝与服务异常测试。WorkBuddy 后置 allow 仍可能覆盖拒绝,Hook 未加载也不能保证停止执行,当前不是不可绕过的受控执行模式。
26
+
27
+ 手工检查与修复入口如下;自定义助手目录时,在 `workbuddy-hooks` 前加 `--config-dir <目录>`。用户修改过的 Hook 不会被覆盖;先核对 status 和应用设置,确需重装时再执行 remove、install。`connect workbuddy` 命令本身只配置模型/MCP,Hook 自动安装属于交互向导步骤。
28
+
29
+ ```sh
30
+ npx @agentguardx/helper workbuddy-hooks status
31
+ npx @agentguardx/helper workbuddy-hooks remove
32
+ npx @agentguardx/helper workbuddy-hooks install
33
+ ```
34
+
35
+ `disconnect workbuddy`、退出登录、切换服务器及 `uninstall` 会按保存的绝对路径移除助手拥有的 Hook,保留其他 Hook 和用户修改并提示;无模型连接记录时也会清理独立安装的 Hook。若归属记录无效或配置无法安全修改,不会把清理报告为成功。Hook 和本机 MCP 保存 Node 与 npm 启动器的绝对路径;npx 缓存被清理后需重新接入,不能保证缓存永久存在。Hook 每次执行仍经过 npm 验签入口,启动失败转换为明确拒绝;这不覆盖宿主未加载 Hook 的情形。
22
36
 
23
37
  启动器每次执行 Go 核心前,使用内置固定公钥验证 originless npm 清单的 RSA-3072/SHA-256 签名,再检查平台二进制与 macOS 钥匙串 helper 的大小和 SHA-256。验证失败拒绝执行,不下载替换公钥。
24
38
 
package/lib/wizard.cjs CHANGED
@@ -101,7 +101,7 @@ function supportedTools() {
101
101
  { label: 'OpenCode(模型+远程 MCP+本机安全工具)', value: 'opencode', present: command('opencode'), install: 'npm i -g opencode-ai' },
102
102
  // WorkBuddy:自定义模型写入 ~/.workbuddy/models.json、远程 MCP 写入 mcp.json(WORKBUDDY_CONFIG_DIR 可改目录)。
103
103
  // 以应用包或该配置目录存在为准;Windows 安装目录按 Electron 默认位置查找,未经 Windows 实机核对。
104
- { label: 'WorkBuddy(模型+远程 MCP+本机安全工具)', value: 'workbuddy', present: apps('WorkBuddy.app', 'Programs/WorkBuddy/WorkBuddy.exe') || fs.existsSync(process.env.WORKBUDDY_CONFIG_DIR || path.join(home, '.workbuddy')), install: '从 WorkBuddy 官网下载安装并启动一次' },
104
+ { label: `WorkBuddy(模型+远程 MCP+本机安全工具${windows ? ';Hook 暂不支持 Windows' : '+执行前 Hook'})`, value: 'workbuddy', present: apps('WorkBuddy.app', 'Programs/WorkBuddy/WorkBuddy.exe') || fs.existsSync(process.env.WORKBUDDY_CONFIG_DIR || path.join(home, '.workbuddy')), install: '从 WorkBuddy 官网下载安装并启动一次' },
105
105
  { label: 'OpenClaw(模型)', value: 'openclaw', present: command('openclaw'), install: '按 OpenClaw 官方说明安装' },
106
106
  { label: 'Cursor(远程 MCP+本机安全工具,需 launch)', value: 'cursor', present: command('cursor') || apps('Cursor.app', 'Programs/Cursor/Cursor.exe'), install: '从 cursor.com 下载安装' },
107
107
  { label: 'Cherry Studio(手动填写说明)', value: 'cherry-studio', present: apps('Cherry Studio.app', 'Programs/Cherry Studio/Cherry Studio.exe'), install: '从 cherry-ai.com 下载安装' },
@@ -129,9 +129,21 @@ async function inDirectory(directory, action) {
129
129
  if (directory) process.chdir(directory);
130
130
  try { return await action(); } finally { if (directory) process.chdir(previous); }
131
131
  }
132
- function clientSummary(tool, record, verification, doctorPassed, commandCode) {
132
+ async function installWorkBuddyHooks(runtime, prefix) {
133
+ let code;
134
+ try { code = await run(runtime, [...prefix, 'workbuddy-hooks', 'install'], ''); }
135
+ catch { return { code: 1, configured: false, reason: 'hook_installation_failed' }; }
136
+ if (code) return { code, configured: false, reason: process.platform === 'win32' ? 'client_hook_platform_unsupported' : 'hook_installation_failed' };
137
+ try {
138
+ const state = await queryJSON(runtime, [...prefix, 'workbuddy-hooks', 'status']);
139
+ if (state?.client === 'workbuddy' && state.hooks_configured === true) return { code: 0, configured: true };
140
+ } catch { /* Configuration readback failed; installation alone is not verification. */ }
141
+ return { code: 1, configured: false, reason: 'hook_configuration_unverified' };
142
+ }
143
+ function clientSummary(tool, record, verification, doctorPassed, commandCode, hook) {
133
144
  const labels = { opencode: 'OpenCode', workbuddy: 'WorkBuddy', openclaw: 'OpenClaw', cursor: 'Cursor', 'cherry-studio': 'Cherry Studio' };
134
- const lines = [`${labels[tool] || tool}:${commandCode ? '接入命令失败,未自动重试' : verification?.configuration_verified ? '配置已核验' : '配置尚未确认'}`];
145
+ const configuration = verification?.configuration_verified ? '配置已核验' : '配置尚未确认';
146
+ const lines = [`${labels[tool] || tool}:${commandCode ? '接入命令失败,未自动重试' : tool === 'workbuddy' ? `模型/MCP ${configuration};Hook ${hook?.configured ? '配置已核验' : '尚未完成'}` : configuration}`];
135
147
  if (record) {
136
148
  let origin; try { origin = serverOrigin(record.gateway_url); } catch { lines.push(' 地址记录无效,需修复后再使用。'); return lines; }
137
149
  const verified = verification?.configuration_verified ? '已核验' : '记录值,尚未核验';
@@ -140,6 +152,10 @@ function clientSummary(tool, record, verification, doctorPassed, commandCode) {
140
152
  if (tool === 'workbuddy') lines.push(` 本机 MCP:agentguard_local;固定受控工作根:${record.workbuddy?.local_workspace || '未确认'}`);
141
153
  else if (['opencode', 'cursor'].includes(tool)) lines.push(' 本机 MCP:agentguard_local;工作根为客户端启动目录,实际运行时仍须核对。');
142
154
  }
155
+ if (tool === 'workbuddy') {
156
+ lines.push(` 执行前 Hook:${hook?.configured ? '配置已核验;宿主加载与真实允许/拒绝调用尚未验证' : hook?.reason === 'client_hook_platform_unsupported' ? '未安装,当前不支持 Windows' : '配置未确认;修复后重新运行接入助手,不自动重试'}`);
157
+ lines.push(' Hook 限制:后置 allow 仍可能覆盖拒绝;Hook 未加载时不能保证停止执行。');
158
+ }
143
159
  lines.push(` doctor:${doctorPassed ? '本轮检查通过' : '未全部通过,详见上方实际逐项结果'}`);
144
160
  lines.push(' 应用内信任、所选 AgentGuard 模型与真实调用结果仍需确认。');
145
161
  return lines;
@@ -277,7 +293,19 @@ async function init(runtime, args) {
277
293
  if (restored) { outcomes.push({ tool, code: restored }); continue; }
278
294
  }
279
295
  const result = await inDirectory(workspace, () => run(runtime, [...target, 'connect', tool], ''));
280
- outcomes.push({ tool, code: result });
296
+ let hook;
297
+ if (tool === 'workbuddy' && result === 0) {
298
+ process.stdout.write('安装并核验 WorkBuddy 执行前 Hook(保留原有模型、MCP 和其他 Hook):\n');
299
+ hook = await installWorkBuddyHooks(runtime, prefix);
300
+ if (hook.code) {
301
+ process.stdout.write(`WorkBuddy 模型/MCP 接入已保留,Hook 未完成:${hook.reason};未自动重试。\n`);
302
+ if (hook.reason !== 'client_hook_platform_unsupported') {
303
+ const repairPrefix = prefix.map(value => `'${value.replace(/'/g, `'"'"'`)}'`).join(' ');
304
+ if (!/[\x00-\x1f\x7f]/.test(repairPrefix)) for (const action of ['status', 'remove', 'install']) process.stdout.write(` ${action === 'status' ? '先检查' : action === 'remove' ? '确认后移除助手原归属条目(保留用户修改)' : '再安装'}:npx @agentguardx/helper ${repairPrefix ? repairPrefix + ' ' : ''}workbuddy-hooks ${action}\n`);
305
+ }
306
+ }
307
+ }
308
+ outcomes.push({ tool, code: result, hook });
281
309
  if (result) process.stdout.write(`${tool} 接入未成功;保留原归属保护,不自动重试,继续汇总其它选中客户端。\n`);
282
310
  } catch (error) {
283
311
  const reason = /^[a-z0-9_]+$/.test(error.message) ? error.message : 'client_setup_failed';
@@ -293,8 +321,8 @@ async function init(runtime, args) {
293
321
  catch { process.stdout.write('无法读回完整配置核验状态,不能确认全部接入完成。\n'); }
294
322
  const clients = new Map((verification.clients || []).map(row => [row.client, row]));
295
323
  process.stdout.write('本次客户端接入结果:\n');
296
- for (const result of outcomes) for (const line of clientSummary(result.tool, records[result.tool], clients.get(result.tool), doctorCode === 0, result.code)) process.stdout.write(line + '\n');
297
- const complete = selected.length > 0 && doctorCode === 0 && outcomes.every(row => row.code === 0 && clients.get(row.tool)?.configuration_verified === true);
324
+ for (const result of outcomes) for (const line of clientSummary(result.tool, records[result.tool], clients.get(result.tool), doctorCode === 0, result.code, result.hook)) process.stdout.write(line + '\n');
325
+ const complete = selected.length > 0 && doctorCode === 0 && outcomes.every(row => row.code === 0 && clients.get(row.tool)?.configuration_verified === true && (row.tool !== 'workbuddy' || row.hook?.configured === true));
298
326
  process.exitCode = complete ? 0 : doctorCode || (selected.length ? 1 : 0);
299
327
  process.stdout.write(complete ? '选中客户端的配置与本轮诊断已通过;请在应用中完成信任和一次真实允许/拒绝调用验证。\n' : selected.length ? '接入尚未全部确认完成,请按每端结果处理;没有自动重试失败操作。\n' : '未选择客户端;已保留本次登录及诊断结果。\n');
300
328
  } finally { process.stdin.pause(); if (typeof process.stdin.unref === 'function') process.stdin.unref(); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentguardx/helper",
3
- "version": "0.3.99",
3
+ "version": "0.3.100",
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.99",
23
- "@agentguardx/helper-darwin-arm64": "0.3.99",
24
- "@agentguardx/helper-linux-x64": "0.3.99",
25
- "@agentguardx/helper-linux-arm64": "0.3.99",
26
- "@agentguardx/helper-win32-x64": "0.3.99",
27
- "@agentguardx/helper-win32-arm64": "0.3.99"
22
+ "@agentguardx/helper-darwin-x64": "0.3.100",
23
+ "@agentguardx/helper-darwin-arm64": "0.3.100",
24
+ "@agentguardx/helper-linux-x64": "0.3.100",
25
+ "@agentguardx/helper-linux-arm64": "0.3.100",
26
+ "@agentguardx/helper-win32-x64": "0.3.100",
27
+ "@agentguardx/helper-win32-arm64": "0.3.100"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public"