@xynogen/pix-gate 0.3.0 → 0.3.2
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/index.ts +20 -5
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -137,10 +137,15 @@ export default function (pi: ExtensionAPI): void {
|
|
|
137
137
|
risky: 1,
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
+
// The unified command gate also covers pix-proc's `proc` tool: its `start`
|
|
141
|
+
// action carries a shell command in `event.input.command`, identical to bash.
|
|
142
|
+
// proc's other actions (list/logs/stop/rm) carry no command and pass straight
|
|
143
|
+
// through. Install pix-proc without pix-gate = ungated, same as bash.
|
|
144
|
+
const GATED_COMMAND_TOOLS = new Set(["bash", "proc"]);
|
|
140
145
|
pi.on("tool_call", async (event, ctx) => {
|
|
141
|
-
if (event.toolName
|
|
146
|
+
if (!GATED_COMMAND_TOOLS.has(String(event.toolName))) return undefined;
|
|
142
147
|
|
|
143
|
-
const command = String(event.input.command ?? "");
|
|
148
|
+
const command = String((event.input as Record<string, unknown>).command ?? "");
|
|
144
149
|
if (!command.trim()) return undefined;
|
|
145
150
|
|
|
146
151
|
// Collect all concerns: path hits + command hit
|
|
@@ -148,6 +153,16 @@ export default function (pi: ExtensionAPI): void {
|
|
|
148
153
|
|
|
149
154
|
// Path concerns
|
|
150
155
|
const candidates = extractPathsFromBash(command);
|
|
156
|
+
|
|
157
|
+
// Steer a blocked ssh-config read toward the dedicated `ssh info` tool
|
|
158
|
+
// (ssh_run action:"info"), which reads the effective config without touching
|
|
159
|
+
// ~/.ssh directly. Only when ssh_run is installed. Appended to the block
|
|
160
|
+
// reason so the model reads it and stops retrying with awk/grep/raw ssh.
|
|
161
|
+
const readsSshConfig = candidates.some((p) => /(^|\/)\.ssh\/config\b/i.test(p));
|
|
162
|
+
const sshInfoSteer =
|
|
163
|
+
readsSshConfig && isRegistered("ssh_run")
|
|
164
|
+
? ' Use the ssh_run tool with action:"info" to read the effective SSH config instead of reading ~/.ssh/config in bash.'
|
|
165
|
+
: "";
|
|
151
166
|
for (const p of candidates) {
|
|
152
167
|
const hit = classifyPath(p, "read", pathRules);
|
|
153
168
|
if (!hit) continue;
|
|
@@ -208,7 +223,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
208
223
|
if (unattended === "deny") {
|
|
209
224
|
return {
|
|
210
225
|
block: true,
|
|
211
|
-
reason: `[AFK][${highest.label}] ${highest.detail}`,
|
|
226
|
+
reason: `[AFK][${highest.label}] ${highest.detail}${sshInfoSteer}`,
|
|
212
227
|
};
|
|
213
228
|
}
|
|
214
229
|
|
|
@@ -219,7 +234,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
219
234
|
if (highest.tier >= SEVERITY_TIER.block) {
|
|
220
235
|
return {
|
|
221
236
|
block: true,
|
|
222
|
-
reason: `[${highest.label}] ${highest.detail} (no UI, auto-blocked)`,
|
|
237
|
+
reason: `[${highest.label}] ${highest.detail} (no UI, auto-blocked)${sshInfoSteer}`,
|
|
223
238
|
};
|
|
224
239
|
}
|
|
225
240
|
return undefined;
|
|
@@ -260,7 +275,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
260
275
|
|
|
261
276
|
if (!decision.approved) {
|
|
262
277
|
ctx.ui.notify(`${highest.icon} ${decision.reason}: ${highest.detail}`, "warning");
|
|
263
|
-
return { block: true, reason: `[${highest.label}] ${decision.reason}` };
|
|
278
|
+
return { block: true, reason: `[${highest.label}] ${decision.reason}${sshInfoSteer}` };
|
|
264
279
|
}
|
|
265
280
|
|
|
266
281
|
const severityColor =
|