@bolloon/bolloon-agent 0.3.50 → 0.3.51

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.
@@ -79,17 +79,17 @@ export class DenyPipeline {
79
79
  }
80
80
  /**
81
81
  * 构建 permission-mode checker.
82
- * default 模式: 禁用 shell_exec / git_commit / git_push 等危险工具
82
+ * default 模式: 禁用 git 写操作 (commit/push/branch "不搞乱")
83
83
  * bypassPermissions: 放行所有
84
84
  *
85
- * 2026-08-10: write_file/edit_file/delete_file 移出 default 禁列表 — 它们已有
86
- * checkWritePath 写入白名单兜底 (self-improve-policy.json), 正常任务 (如写 HTML 发布
87
- * IPFS 网站) 不该被 permission 层拦截; 实测日志: "write_file 被权限拦了" → LLM 只能
88
- * 绕道, 任务无法推进.
85
+ * 2026-08-10: shell_exec / write_file / edit_file / delete_file / terminal 移出 default
86
+ * 禁列表 它们已有各自护栏 (shell_exec 命令白名单 / terminal denylist-only /
87
+ * write_file checkWritePath), 用户明确"灵活一点, 少围栏, 核心的东西不碰不搞乱就行";
88
+ * 实测日志 "write_file 被权限拦了" → LLM 只能绕道, 任务无法推进.
89
89
  */
90
90
  static permissionChecker() {
91
91
  const DEFAULT_DENY_TOOLS = new Set([
92
- 'shell_exec', 'git_commit', 'git_push', 'git_branch',
92
+ 'git_commit', 'git_push', 'git_branch',
93
93
  ]);
94
94
  return (ctx) => {
95
95
  if (ctx.permissionMode === 'bypassPermissions') {
@@ -649,6 +649,42 @@ export function registerBuiltinTools(ctx) {
649
649
  return { success: true, output: result.output };
650
650
  }
651
651
  });
652
+ // 2026-08-10: terminal — 灵活终端写命令 (用户要求: bolloon 自己写命令进 terminal, 少围栏).
653
+ // 与 shell_exec 的区别: 接受**完整 shell 命令字符串** (管道/重定向/写文件都行),
654
+ // 护栏只挡高危破坏模式 (checkTerminalCommand: 提权/格式化/删根/.bolloon 数据), 其余放行.
655
+ ctx.tools.set('terminal', {
656
+ name: 'terminal',
657
+ description: '执行完整 shell 命令 (支持管道/重定向/写文件/跑脚本). 护栏只挡高危破坏操作 (sudo/格式化/rm -rf 根目录/写 ~/.bolloon 数据), 其余灵活放行. 适合: 写 HTML 文件、跑 python/node 脚本、查系统状态、装依赖.',
658
+ parameters: { command: '完整 shell 命令 (必填, 如: echo "<html>" > /tmp/site/index.html && ls /tmp/site)', timeoutMs: '超时毫秒, 默认 30000' },
659
+ execute: async (args) => {
660
+ const raw = String(args.command || '').trim();
661
+ if (!raw)
662
+ return { success: false, error: 'command 必填' };
663
+ const { checkTerminalCommand } = await import('./shell-guard.js');
664
+ const guard = checkTerminalCommand(raw);
665
+ if (!guard.allowed) {
666
+ return { success: false, error: `[terminal-guard] ${guard.reason}`, deniedByGuard: true };
667
+ }
668
+ const { exec } = await import('child_process');
669
+ const timeoutMs = Number(args.timeoutMs) || 30000;
670
+ return new Promise((resolve) => {
671
+ exec(raw, {
672
+ cwd: process.cwd(),
673
+ timeout: timeoutMs,
674
+ maxBuffer: 8 * 1024 * 1024,
675
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
676
+ }, (err, stdout, stderr) => {
677
+ const output = ((stdout || '') + (stderr ? `\n[stderr]\n${stderr}` : '')).trim().slice(0, 8000);
678
+ if (err) {
679
+ resolve({ success: false, error: `exit ${err.code ?? '?'}: ${String(err.message || '').slice(0, 200)}`, output: output || undefined });
680
+ }
681
+ else {
682
+ resolve({ success: true, output: output || '(无输出)' });
683
+ }
684
+ });
685
+ });
686
+ }
687
+ });
652
688
  // self_improve
653
689
  ctx.tools.set('self_improve', {
654
690
  name: 'self_improve',
@@ -335,6 +335,44 @@ export const SELF_IMPROVE_COOLDOWN_MS = 6 * 60 * 60 * 1000;
335
335
  /** @deprecated 用 getSandboxCwd() */
336
336
  export const SHELL_SANDBOX_CWD = path.resolve(process.cwd(), '.bolloon-shell-sandbox');
337
337
  // ============================================================================
338
+ // terminal 工具宽松护栏 (2026-08-10) — denylist-only, 不碰核心即可
339
+ // 与 checkCommand 的区别: 不查命令白名单/参数 allowlist — 完整 shell 命令字符串放行,
340
+ // 只挡"高危破坏/碰核心数据"模式. 用户明确: 灵活一点, 少围栏, 核心的东西不碰不搞乱.
341
+ // ============================================================================
342
+ const TERMINAL_DENY_PATTERNS = [
343
+ /\bsudo\b/, // 提权
344
+ /\bsu\b/,
345
+ /\bmkfs\b/, // 格式化
346
+ /\bshred\b/, // 擦除
347
+ /\bdd\s+.*of=\/dev\//, // 写设备
348
+ /\brm\s+-(rf|fr)\b/, // 递归删除 (rm 单个文件允许)
349
+ /\bchmod\s+-R\s+777\b/, // 全权限
350
+ /\bcurl\b[^|]*\|\s*(sh|bash)\b/, // curl|sh 远程执行
351
+ /\bwget\b[^|]*\|\s*(sh|bash)\b/,
352
+ /\b:\(\)\s*\{[^}]*\}\s*;\s*:/, // fork bomb
353
+ /\b(>|>>)\s*(\/etc\/|\/usr\/|\/System\/|\/bin\/|\/sbin\/)/, // 写系统目录
354
+ /[\/\s]\.bolloon\b/, // Bolloon 数据 (sessions/persona/keys; 覆盖 ~/.bolloon, $HOME/.bolloon, 空格.bolloon)
355
+ /[\/\s]\.(diap|hermes|openclaw)\b/, // 其他 agent 数据
356
+ /\brm\s+-rf\s+(\/|~|\*|\.|\$HOME)\b/, // 删根/家/通配
357
+ /\bgit\s+push\b[^|]*\s+(-f|--force)/, // 强推
358
+ /\bgit\s+reset\s+--hard\b/,
359
+ /\bkill\s+-9\s+\d+\b/, // 杀进程 (保留给用户)
360
+ ];
361
+ /** 检查完整 shell 命令 (terminal 工具用). denylist-only: 命中高危模式即拒, 否则放行. */
362
+ export function checkTerminalCommand(rawCmd) {
363
+ const cmd = (rawCmd || '').trim();
364
+ if (!cmd)
365
+ return { allowed: false, reason: '命令为空', matchedBy: 'fallback-deny' };
366
+ for (const pattern of TERMINAL_DENY_PATTERNS) {
367
+ if (pattern.test(cmd)) {
368
+ auditShellCall('denied', cmd, [], `terminal 高危模式 ${pattern}`);
369
+ return { allowed: false, reason: `命令命中高危护栏 ${pattern} (核心不碰: 提权/格式化/删根/.bolloon 数据). 换一条安全命令.`, matchedBy: 'arg-denylist' };
370
+ }
371
+ }
372
+ auditShellCall('allowed', cmd, []);
373
+ return { allowed: true };
374
+ }
375
+ // ============================================================================
338
376
  // 写策略 / 审计路径 (供 API 端点用)
339
377
  // ============================================================================
340
378
  export const POLICY_AUDIT_PATH_PUBLIC = POLICY_AUDIT_PATH;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolloon/bolloon-agent",
3
- "version": "0.3.50",
3
+ "version": "0.3.51",
4
4
  "type": "module",
5
5
  "description": "P2P AI Document Agent - 全局安装后执行 `bolloon` 启动产品",
6
6
  "main": "dist/cli-entry.js",