@trim21/personal-pi-extensions 0.0.426 → 0.0.427

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
@@ -80,8 +80,8 @@ bash 工具(opencode 风格 `bash`、Claude Code 风格 `Bash`)注册了 `da
80
80
  "writablePaths": [".", "/tmp", "~/my-projects"],
81
81
  // 额外可写路径,与默认值合并(ro-bind)
82
82
  "extraWritablePaths": ["~/.config"],
83
- // tmpfs 挂载路径(避免写入磁盘)
84
- "tmpfsPaths": [],
83
+ // 沙箱内隐藏的路径:以 / 结尾的视为目录(挂空 tmpfs),否则按文件处理(--ro-bind-try /dev/null)
84
+ "denyPaths": [],
85
85
  // 额外 bwrap 参数
86
86
  "extraArgs": ["--die-with-parent"],
87
87
  // 全权限执行的自动审批规则:命中规则的命令不弹确认框
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.0.426",
3
+ "version": "0.0.427",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
package/src/bwrap/core.ts CHANGED
@@ -31,7 +31,12 @@ const bwrapConfigProperties = {
31
31
  bwrapPath: Type.Optional(Type.String()),
32
32
  writablePaths: Type.Array(Type.String()),
33
33
  extraWritablePaths: Type.Array(Type.String()),
34
- tmpfsPaths: Type.Array(Type.String()),
34
+ denyPaths: Type.Array(
35
+ Type.String({
36
+ description:
37
+ "沙箱内隐藏的路径:以 / 结尾为目录(挂空 tmpfs),否则为文件(--ro-bind-try /dev/null)",
38
+ }),
39
+ ),
35
40
  extraArgs: Type.Array(Type.String()),
36
41
  networkAllowlist: Type.Array(
37
42
  Type.String({ description: "允许直连的域名 / IP / CIDR,可带 :port" }),
@@ -71,7 +76,8 @@ export interface ResolvedBwrap {
71
76
  bwrapPath?: string;
72
77
  writablePaths: string[];
73
78
  extraWritablePaths: string[];
74
- tmpfsPaths: string[];
79
+ /** 沙箱内隐藏的路径:以 / 结尾为目录(挂空 tmpfs),否则为文件(--ro-bind-try /dev/null)。 */
80
+ denyPaths: string[];
75
81
  extraArgs: string[];
76
82
  /** 允许直连的域名 / IP / IP:port 白名单(非空 = 启用 sing-box 网络过滤)。 */
77
83
  networkAllowlist: string[];
@@ -85,7 +91,7 @@ const DEFAULT_CONFIG: BwrapConfig = {
85
91
  mode: "workspace-write",
86
92
  writablePaths: [".", "/tmp"],
87
93
  extraWritablePaths: [],
88
- tmpfsPaths: [],
94
+ denyPaths: [],
89
95
  extraArgs: [],
90
96
  networkAllowlist: [],
91
97
  };
@@ -96,7 +102,7 @@ export function resolveBwrap(config: BwrapConfig): ResolvedBwrap {
96
102
  bwrapPath: config.bwrapPath,
97
103
  writablePaths: config.writablePaths ?? [".", "/tmp"],
98
104
  extraWritablePaths: config.extraWritablePaths,
99
- tmpfsPaths: config.tmpfsPaths ?? [],
105
+ denyPaths: config.denyPaths ?? [],
100
106
  extraArgs: config.extraArgs ?? [],
101
107
  networkAllowlist: config.networkAllowlist ?? [],
102
108
  singBoxPath: config.singBoxPath,
@@ -128,7 +134,7 @@ export function resolveHeadlessBwrap(config: BwrapConfig): ResolvedBwrap {
128
134
  mode: "readonly",
129
135
  writablePaths: [],
130
136
  extraWritablePaths: [],
131
- tmpfsPaths: [],
137
+ denyPaths: [],
132
138
  extraArgs: [],
133
139
  });
134
140
  }
@@ -139,7 +145,7 @@ function deepMerge(base: BwrapConfig, overrides: Partial<BwrapConfig>): BwrapCon
139
145
  bwrapPath: overrides.bwrapPath ?? base.bwrapPath,
140
146
  writablePaths: overrides.writablePaths ?? base.writablePaths,
141
147
  extraWritablePaths: [...base.extraWritablePaths, ...(overrides.extraWritablePaths ?? [])],
142
- tmpfsPaths: overrides.tmpfsPaths ?? base.tmpfsPaths,
148
+ denyPaths: overrides.denyPaths ?? base.denyPaths,
143
149
  extraArgs: overrides.extraArgs ?? base.extraArgs,
144
150
  networkAllowlist: overrides.networkAllowlist ?? base.networkAllowlist,
145
151
  singBoxPath: overrides.singBoxPath ?? base.singBoxPath,
@@ -323,8 +329,14 @@ export async function buildBwrapArgs(resolved: ResolvedBwrap, cwd: string): Prom
323
329
  const absolutePath = resolveBwrapPath(path, cwd);
324
330
  args.push("--bind-try", absolutePath, absolutePath);
325
331
  }
326
- for (const path of resolved.tmpfsPaths) {
327
- args.push("--tmpfs", resolveBwrapPath(path, cwd));
332
+ // denyPaths:以 / 结尾的条目视为目录(挂空 tmpfs),否则视为文件(--ro-bind-try /dev/null 覆盖)
333
+ for (const path of resolved.denyPaths) {
334
+ const target = resolveBwrapPath(path, cwd);
335
+ if (path.endsWith("/")) {
336
+ args.push("--tmpfs", target);
337
+ } else {
338
+ args.push("--ro-bind-try", "/dev/null", target);
339
+ }
328
340
  }
329
341
  if (!resolved.network) args.push("--unshare-net");
330
342
  // --ro-bind-try:目录不存在(或已被删除)时自动忽略
@@ -708,9 +708,9 @@ export class BwrapRuntime {
708
708
  const writable = runtime.writablePaths.map((path) =>
709
709
  resolveBwrapPath(path, commandCtx.cwd),
710
710
  );
711
- const tmpfs = runtime.tmpfsPaths.map((path) => resolveBwrapPath(path, commandCtx.cwd));
711
+ const deny = runtime.denyPaths.map((path) => resolveBwrapPath(path, commandCtx.cwd));
712
712
  commandCtx.ui.notify(
713
- `bwrap ${runtime.mode} ${runtime.network ? "net" : "no-net"} write:[${writable.join(", ")}] tmpfs:[${tmpfs.join(", ") || "-"}]`,
713
+ `bwrap ${runtime.mode} ${runtime.network ? "net" : "no-net"} write:[${writable.join(", ")}] deny:[${deny.join(", ") || "-"}]`,
714
714
  "info",
715
715
  );
716
716
  }),