@trim21/personal-pi-extensions 0.1.523 → 0.1.525
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/package.json +1 -1
- package/src/bwrap/core.ts +22 -7
- package/src/claude-code/bash.md +1 -1
- package/src/claude-code/shell.ts +3 -3
- package/src/opencode/bash.ts +3 -4
package/package.json
CHANGED
package/src/bwrap/core.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ChildProcess, spawn } from "node:child_process";
|
|
2
2
|
import { constants, type Dirent, existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import { access as fsAccess, readdir, stat } from "node:fs/promises";
|
|
3
|
+
import { access as fsAccess, readdir, realpath, stat } from "node:fs/promises";
|
|
4
4
|
import { delimiter, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
@@ -337,20 +337,34 @@ export async function findGitDirs(root: string): Promise<string[]> {
|
|
|
337
337
|
return found;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
/**
|
|
341
|
+
* bwrap 创建挂载点时不跟随目标路径中的 symlink 组件(防 symlink 逃逸),
|
|
342
|
+
* 含绝对 symlink 的配置路径会以 "Can't mkdir parents ... No such file or directory" 失败。
|
|
343
|
+
* 已存在的路径先解析成真实路径(沙箱内 ro-bind 的 / 下同样可见,symlink 语义不变);
|
|
344
|
+
* 不存在的路径保持原样,交给 --*-try 的跳过语义处理。
|
|
345
|
+
*/
|
|
346
|
+
async function realpathOrSelf(path: string): Promise<string> {
|
|
347
|
+
try {
|
|
348
|
+
return await realpath(path);
|
|
349
|
+
} catch {
|
|
350
|
+
return path;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
340
354
|
export async function buildBwrapArgs(resolved: ResolvedBwrap, cwd: string): Promise<string[]> {
|
|
341
355
|
const args = ["--new-session", "--die-with-parent", "--unshare-user", "--unshare-pid"];
|
|
342
356
|
// --*-bind-try:配置的路径不存在时忽略该项而不是让整条命令失败
|
|
343
357
|
for (const path of resolved.writablePaths) {
|
|
344
|
-
const absolutePath = resolveBwrapPath(path, cwd);
|
|
358
|
+
const absolutePath = await realpathOrSelf(resolveBwrapPath(path, cwd));
|
|
345
359
|
args.push("--bind-try", absolutePath, absolutePath);
|
|
346
360
|
}
|
|
347
361
|
for (const path of resolved.extraWritablePaths) {
|
|
348
|
-
const absolutePath = resolveBwrapPath(path, cwd);
|
|
362
|
+
const absolutePath = await realpathOrSelf(resolveBwrapPath(path, cwd));
|
|
349
363
|
args.push("--bind-try", absolutePath, absolutePath);
|
|
350
364
|
}
|
|
351
365
|
// denyPaths:以 / 结尾的条目视为目录(挂空 tmpfs),否则视为文件(--ro-bind-try /dev/null 覆盖)
|
|
352
366
|
for (const path of resolved.denyPaths) {
|
|
353
|
-
const target = resolveBwrapPath(path, cwd);
|
|
367
|
+
const target = await realpathOrSelf(resolveBwrapPath(path, cwd));
|
|
354
368
|
if (path.endsWith("/")) {
|
|
355
369
|
args.push("--tmpfs", target);
|
|
356
370
|
} else {
|
|
@@ -360,13 +374,13 @@ export async function buildBwrapArgs(resolved: ResolvedBwrap, cwd: string): Prom
|
|
|
360
374
|
if (!resolved.network) args.push("--unshare-net");
|
|
361
375
|
// --ro-bind-try:目录不存在(或已被删除)时自动忽略
|
|
362
376
|
for (const name of PROTECTED_DIRS) {
|
|
363
|
-
const absolutePath = join(cwd, name);
|
|
377
|
+
const absolutePath = await realpathOrSelf(join(cwd, name));
|
|
364
378
|
args.push("--ro-bind-try", absolutePath, absolutePath);
|
|
365
379
|
}
|
|
366
380
|
// 工作区下所有 .git 一律只读:可写 bind 之上的覆盖绑定,防止命令篡改仓库元数据。
|
|
367
381
|
// 根目录本身是 git 仓库时只保护根 .git(递归扫描有成本,绝大多数情况根即唯一仓库);
|
|
368
382
|
// 根不是 git 仓库时才递归扫描嵌套仓库(如 monorepo 子仓库)。
|
|
369
|
-
const rootGit = join(cwd, ".git");
|
|
383
|
+
const rootGit = await realpathOrSelf(join(cwd, ".git"));
|
|
370
384
|
let gitDirs: string[];
|
|
371
385
|
try {
|
|
372
386
|
await stat(rootGit);
|
|
@@ -375,7 +389,8 @@ export async function buildBwrapArgs(resolved: ResolvedBwrap, cwd: string): Prom
|
|
|
375
389
|
gitDirs = await findGitDirs(cwd);
|
|
376
390
|
}
|
|
377
391
|
for (const gitDir of gitDirs) {
|
|
378
|
-
|
|
392
|
+
const realGitDir = await realpathOrSelf(gitDir);
|
|
393
|
+
args.push("--ro-bind-try", realGitDir, realGitDir);
|
|
379
394
|
}
|
|
380
395
|
args.push(...resolved.extraArgs);
|
|
381
396
|
return args;
|
package/src/claude-code/bash.md
CHANGED
|
@@ -18,7 +18,7 @@ IMPORTANT: Avoid using this tool to run `find`, `grep`, `cat`, `head`, `tail`, `
|
|
|
18
18
|
- Communication: Output text directly (NOT echo/printf)
|
|
19
19
|
While the Bash tool can do similar things, it's better to use the built-in tools as they provide a better user experience and make it easier to review tool calls and give permission.
|
|
20
20
|
|
|
21
|
-
- You may specify an optional timeout in milliseconds (up to
|
|
21
|
+
- You may specify an optional timeout in milliseconds (up to 7200000ms / 2 hours). By default, your command will timeout after 120000ms (2 minutes).
|
|
22
22
|
- Prever to use workdir argument over `cd ...`
|
|
23
23
|
- When issuing multiple commands:
|
|
24
24
|
- If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. Example: if you need to run "git status" and "git diff", send a single message with two Bash tool calls in parallel.
|
package/src/claude-code/shell.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { BashInterruptedError, type BwrapRuntime, createBwrapRuntime } from "../
|
|
|
14
14
|
import { resolveWorkdir } from "../lib/path.js";
|
|
15
15
|
|
|
16
16
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
17
|
-
const MAX_TIMEOUT_MS =
|
|
17
|
+
const MAX_TIMEOUT_MS = 7_200_000;
|
|
18
18
|
|
|
19
19
|
/** 对齐 Claude Code formatError:错误文本超过该长度时头尾各保留一半。 */
|
|
20
20
|
const MAX_ERROR_CHARS = 10_000;
|
|
@@ -89,14 +89,14 @@ export function registerShellTools(
|
|
|
89
89
|
label: "Bash",
|
|
90
90
|
description: [
|
|
91
91
|
"Executes a given bash command synchronously and returns its output.",
|
|
92
|
-
"timeout is in milliseconds, defaults to 120000, and may not exceed
|
|
92
|
+
"timeout is in milliseconds, defaults to 120000, and may not exceed 7200000.",
|
|
93
93
|
"Every command runs in the foreground. Background command execution is not supported; shell jobs are waited for before the tool returns.",
|
|
94
94
|
].join("\n"),
|
|
95
95
|
parameters: Type.Object(
|
|
96
96
|
{
|
|
97
97
|
command: Type.String({ description: "The command to execute" }),
|
|
98
98
|
timeout: Type.Optional(
|
|
99
|
-
Type.Number({ description: "Optional timeout in milliseconds (max
|
|
99
|
+
Type.Number({ description: "Optional timeout in milliseconds (max 7200000)" }),
|
|
100
100
|
),
|
|
101
101
|
description: Type.Optional(
|
|
102
102
|
Type.String({ description: "Clear, concise description of the command" }),
|
package/src/opencode/bash.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { BashInterruptedError, type BwrapRuntime, createBwrapRuntime } from "../
|
|
|
8
8
|
import { resolveWorkdir } from "../lib/path.js";
|
|
9
9
|
|
|
10
10
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
11
|
-
const MAX_TIMEOUT_MS =
|
|
11
|
+
const MAX_TIMEOUT_MS = 7_200_000;
|
|
12
12
|
|
|
13
13
|
/** 对齐上游 opencode 的截断提示文案(tools/BashTool/bash.ts)。 */
|
|
14
14
|
const CAPTURE_TRUNCATED_NOTICE = "[output capture truncated at the in-memory safety limit]";
|
|
@@ -46,7 +46,7 @@ export default function opencodeBash(
|
|
|
46
46
|
description: [
|
|
47
47
|
"Executes a given bash command synchronously and returns its output.",
|
|
48
48
|
"The default working directory is the current directory; use workdir to run elsewhere.",
|
|
49
|
-
"timeout is in milliseconds, defaults to 120000, and may not exceed
|
|
49
|
+
"timeout is in milliseconds, defaults to 120000, and may not exceed 7200000.",
|
|
50
50
|
"Every command runs in the foreground. Background command execution is not supported; shell jobs are waited for before the tool returns.",
|
|
51
51
|
].join("\n"),
|
|
52
52
|
promptSnippet: "execute bash command",
|
|
@@ -65,8 +65,7 @@ export default function opencodeBash(
|
|
|
65
65
|
),
|
|
66
66
|
timeout: Type.Optional(
|
|
67
67
|
Type.Number({
|
|
68
|
-
description: "Optional timeout in milliseconds (max
|
|
69
|
-
default: 600,
|
|
68
|
+
description: "Optional timeout in milliseconds (max 7200000)",
|
|
70
69
|
}),
|
|
71
70
|
),
|
|
72
71
|
dangerouslyDisableSandbox: Type.Optional(
|