@trim21/personal-pi-extensions 0.0.222 → 0.0.224
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 +2 -1
- package/src/bwrap/index.ts +0 -2
- package/src/claude-code/shell.ts +31 -48
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trim21/personal-pi-extensions",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.224",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
|
|
6
6
|
"keywords": [
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"src/session-name.ts",
|
|
59
59
|
"src/bwrap/index.ts",
|
|
60
60
|
"src/workspace-guard.ts",
|
|
61
|
+
"src/claude-code/index.ts",
|
|
61
62
|
"src/opencode/index.ts",
|
|
62
63
|
"src/bash-default-timeout.ts",
|
|
63
64
|
"src/gh-readonly.ts",
|
package/src/bwrap/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { createBashTool } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
|
-
import { Value } from "typebox/value";
|
|
5
4
|
|
|
6
5
|
import { bwrapRuntime } from "./runtime.js";
|
|
7
6
|
|
|
@@ -40,7 +39,6 @@ export default function bwrapExtension(pi: ExtensionAPI): void {
|
|
|
40
39
|
localBash.description +
|
|
41
40
|
"\n\nSet request_full_access to true to request unsandboxed execution.",
|
|
42
41
|
parameters: sandboxedBashSchema,
|
|
43
|
-
prepareArguments: (args) => Value.Parse(sandboxedBashSchema, args),
|
|
44
42
|
executionMode: localBash.executionMode,
|
|
45
43
|
execute(id, params, signal, onUpdate, ctx) {
|
|
46
44
|
return bwrapRuntime.execute({
|
package/src/claude-code/shell.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
|
+
|
|
1
4
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
5
|
import { Type } from "typebox";
|
|
3
6
|
|
|
@@ -6,36 +9,26 @@ import { bwrapRuntime } from "../bwrap/runtime.js";
|
|
|
6
9
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
7
10
|
const MAX_TIMEOUT_MS = 600_000;
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return [
|
|
22
|
-
command,
|
|
23
|
-
"__pi_cc_status=$?",
|
|
24
|
-
"wait",
|
|
25
|
-
String.raw`printf '\n${marker}%s${marker}\n' "$PWD"`,
|
|
26
|
-
"exit $__pi_cc_status",
|
|
27
|
-
].join("\n");
|
|
12
|
+
async function resolveWorkdir(workdir: string, cwd: string): Promise<string> {
|
|
13
|
+
const target = isAbsolute(workdir) ? workdir : resolve(cwd, workdir);
|
|
14
|
+
let info;
|
|
15
|
+
try {
|
|
16
|
+
info = await stat(target);
|
|
17
|
+
} catch {
|
|
18
|
+
throw new Error(`Working directory does not exist: ${target}`);
|
|
19
|
+
}
|
|
20
|
+
if (!info.isDirectory()) {
|
|
21
|
+
throw new Error(`Working directory is not a directory: ${target}`);
|
|
22
|
+
}
|
|
23
|
+
return target;
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
export function registerShellTools(pi: ExtensionAPI): void {
|
|
31
|
-
let persistentCwd: string | undefined;
|
|
32
|
-
|
|
33
27
|
pi.registerTool({
|
|
34
28
|
name: "Bash",
|
|
35
29
|
label: "Bash",
|
|
36
30
|
description: [
|
|
37
31
|
"Executes a given bash command synchronously and returns its output.",
|
|
38
|
-
"The working directory persists between commands, but shell state does not.",
|
|
39
32
|
"timeout is in milliseconds, defaults to 120000, and may not exceed 600000.",
|
|
40
33
|
"Every command runs in the foreground. Background command execution is not supported; shell jobs are waited for before the tool returns.",
|
|
41
34
|
].join("\n"),
|
|
@@ -48,6 +41,12 @@ export function registerShellTools(pi: ExtensionAPI): void {
|
|
|
48
41
|
description: Type.Optional(
|
|
49
42
|
Type.String({ description: "Clear, concise description of the command" }),
|
|
50
43
|
),
|
|
44
|
+
workdir: Type.Optional(
|
|
45
|
+
Type.String({
|
|
46
|
+
description:
|
|
47
|
+
"Working directory to execute the command in. Defaults to the current directory; relative paths resolve from there.",
|
|
48
|
+
}),
|
|
49
|
+
),
|
|
51
50
|
dangerouslyDisableSandbox: Type.Optional(
|
|
52
51
|
Type.Boolean({
|
|
53
52
|
description:
|
|
@@ -63,43 +62,27 @@ export function registerShellTools(pi: ExtensionAPI): void {
|
|
|
63
62
|
throw new Error(`timeout must be between 1 and ${MAX_TIMEOUT_MS} milliseconds`);
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
const
|
|
65
|
+
const cwd = params.workdir ? await resolveWorkdir(params.workdir, ctx.cwd) : ctx.cwd;
|
|
66
|
+
|
|
67
67
|
try {
|
|
68
|
-
|
|
69
|
-
ctx: { ...ctx, cwd
|
|
68
|
+
return await bwrapRuntime.execute({
|
|
69
|
+
ctx: { ...ctx, cwd },
|
|
70
70
|
toolCallId: id,
|
|
71
|
-
command:
|
|
71
|
+
command: params.command,
|
|
72
72
|
timeout: timeout / 1000,
|
|
73
73
|
requestFullAccess: params.dangerouslyDisableSandbox,
|
|
74
74
|
requestFullAccessReason: params.description,
|
|
75
75
|
signal,
|
|
76
|
-
onUpdate
|
|
77
|
-
? (update) => {
|
|
78
|
-
const content = update.content.map((item) => {
|
|
79
|
-
if (item.type !== "text") return item;
|
|
80
|
-
return { ...item, text: stripCwdMarker(item.text, marker).text };
|
|
81
|
-
});
|
|
82
|
-
onUpdate({ ...update, content });
|
|
83
|
-
}
|
|
84
|
-
: undefined,
|
|
85
|
-
});
|
|
86
|
-
const content = result.content.map((item) => {
|
|
87
|
-
if (item.type !== "text") return item;
|
|
88
|
-
const cleaned = stripCwdMarker(item.text, marker);
|
|
89
|
-
if (cleaned.cwd) persistentCwd = cleaned.cwd;
|
|
90
|
-
return { ...item, text: cleaned.text || "(no output)" };
|
|
76
|
+
onUpdate,
|
|
91
77
|
});
|
|
92
|
-
return { ...result, content };
|
|
93
78
|
} catch (error) {
|
|
94
79
|
if (!(error instanceof Error)) throw error;
|
|
95
|
-
const
|
|
96
|
-
if (cleaned.cwd) persistentCwd = cleaned.cwd;
|
|
97
|
-
const timeoutMatch = /Command timed out after [\d.]+ seconds/.exec(cleaned.text);
|
|
80
|
+
const timeoutMatch = /Command timed out after [\d.]+ seconds/.exec(error.message);
|
|
98
81
|
const message = timeoutMatch
|
|
99
|
-
?
|
|
82
|
+
? error.message.slice(0, timeoutMatch.index) +
|
|
100
83
|
`Command timed out after ${timeout} milliseconds` +
|
|
101
|
-
|
|
102
|
-
:
|
|
84
|
+
error.message.slice(timeoutMatch.index + timeoutMatch[0].length)
|
|
85
|
+
: error.message;
|
|
103
86
|
throw new Error(message, { cause: error });
|
|
104
87
|
}
|
|
105
88
|
},
|