@diegopetrucci/pi-extensions 0.1.65 → 0.1.66
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/.pi-fleet-tested-version +1 -1
- package/README.md +1 -1
- package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
- package/extensions/agent-workflow-audit/package.json +1 -1
- package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-git-diff/README.md +3 -0
- package/extensions/annotate-git-diff/index.ts +49 -5
- package/extensions/annotate-git-diff/package.json +2 -1
- package/extensions/annotate-git-diff/quiet-glimpse.ts +24 -3
- package/extensions/annotate-git-diff/review-server.ts +379 -0
- package/extensions/annotate-git-diff/ui.ts +17 -41
- package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-last-message/index.ts +28 -2
- package/extensions/annotate-last-message/package.json +1 -1
- package/extensions/annotate-last-message/quiet-glimpse.ts +24 -3
- package/extensions/brrr/.pi-fleet-tested-version +1 -1
- package/extensions/brrr/package.json +1 -1
- package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
- package/extensions/claude-fast/package.json +1 -1
- package/extensions/code-reviewer/.pi-fleet-tested-version +1 -1
- package/extensions/code-reviewer/package.json +1 -1
- package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
- package/extensions/confirm-destructive/package.json +1 -1
- package/extensions/context-cap/.pi-fleet-tested-version +1 -1
- package/extensions/context-cap/package.json +1 -1
- package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/contrarian/.pi-fleet-tested-version +1 -1
- package/extensions/contrarian/index.ts +46 -17
- package/extensions/contrarian/package.json +1 -1
- package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
- package/extensions/dirty-repo-guard/package.json +1 -1
- package/extensions/dynamic-context-pruning/.pi-fleet-tested-version +1 -1
- package/extensions/dynamic-context-pruning/package.json +1 -1
- package/extensions/fast/.pi-fleet-tested-version +1 -1
- package/extensions/fast/package.json +1 -1
- package/extensions/git-footer/.pi-fleet-tested-version +1 -1
- package/extensions/git-footer/package.json +1 -1
- package/extensions/gnosis/.pi-fleet-tested-version +1 -1
- package/extensions/gnosis/package.json +1 -1
- package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
- package/extensions/illustrations-to-explain-things/package.json +1 -1
- package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
- package/extensions/inline-bash/package.json +1 -1
- package/extensions/librarian/.pi-fleet-tested-version +1 -1
- package/extensions/librarian/package.json +1 -1
- package/extensions/minimal-footer/.pi-fleet-tested-version +1 -1
- package/extensions/minimal-footer/package.json +1 -1
- package/extensions/notify/.pi-fleet-tested-version +1 -1
- package/extensions/notify/package.json +1 -1
- package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
- package/extensions/openai-fast/package.json +1 -1
- package/extensions/oracle/.pi-fleet-tested-version +1 -1
- package/extensions/oracle/index.ts +46 -17
- package/extensions/oracle/package.json +1 -1
- package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
- package/extensions/permission-gate/README.md +11 -3
- package/extensions/permission-gate/index.ts +70 -7
- package/extensions/permission-gate/package.json +4 -2
- package/extensions/permission-gate/powershell-safety.ts +1006 -0
- package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
- package/extensions/quiet-tools/package.json +1 -1
- package/extensions/review/.pi-fleet-tested-version +1 -1
- package/extensions/review/package.json +1 -1
- package/extensions/todo/.pi-fleet-tested-version +1 -1
- package/extensions/todo/package.json +1 -1
- package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
- package/extensions/triage-comments/package.json +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
0.84.
|
|
1
|
+
0.84.4
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.84.
|
|
1
|
+
0.84.4
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.84.
|
|
1
|
+
0.84.4
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.84.
|
|
1
|
+
0.84.4
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.84.
|
|
1
|
+
0.84.4
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-openai-fast",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "A pi extension that enables OpenAI Codex Fast mode for ChatGPT-auth GPT-5.5 and GPT-5.6 Codex variants by injecting the priority service tier.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.84.
|
|
1
|
+
0.84.4
|
|
@@ -115,6 +115,9 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
115
115
|
"claude-opus-4-5",
|
|
116
116
|
"claude-opus-4-1",
|
|
117
117
|
"claude-opus-4",
|
|
118
|
+
"openai.gpt-5.6-sol",
|
|
119
|
+
"openai.gpt-5.6-terra",
|
|
120
|
+
"openai.gpt-5.6-luna",
|
|
118
121
|
"claude-sonnet-5",
|
|
119
122
|
"claude-sonnet-4-6",
|
|
120
123
|
"claude-sonnet-4-5",
|
|
@@ -125,6 +128,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
125
128
|
"minimax-m2.5",
|
|
126
129
|
"minimax-m2.1",
|
|
127
130
|
"zai.glm-5",
|
|
131
|
+
"xai.grok-4.6",
|
|
128
132
|
],
|
|
129
133
|
anthropic: [
|
|
130
134
|
"claude-opus-5",
|
|
@@ -160,6 +164,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
160
164
|
baseten: [
|
|
161
165
|
"moonshotai/Kimi-K3",
|
|
162
166
|
"moonshotai/Kimi-K2.7-Code",
|
|
167
|
+
"zai-org/GLM-5.3",
|
|
163
168
|
"deepseek-ai/DeepSeek-V4-Pro-0813",
|
|
164
169
|
"deepseek-ai/DeepSeek-V4-Pro",
|
|
165
170
|
"zai-org/GLM-5.2",
|
|
@@ -176,43 +181,49 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
176
181
|
"zai-org/GLM-5.2-Fast",
|
|
177
182
|
"zai-org/GLM-4.7",
|
|
178
183
|
],
|
|
179
|
-
cerebras: ["gpt-oss-120b", "
|
|
184
|
+
cerebras: ["gpt-oss-120b", "gemma-4-31b"],
|
|
180
185
|
"cloudflare-ai-gateway": [
|
|
181
186
|
"claude-fable-5",
|
|
182
|
-
"claude-opus-
|
|
183
|
-
"claude-opus-4
|
|
184
|
-
"claude-opus-4
|
|
187
|
+
"claude-opus-5",
|
|
188
|
+
"claude-opus-4.8",
|
|
189
|
+
"claude-opus-4.7",
|
|
190
|
+
"claude-opus-4.6",
|
|
191
|
+
"claude-opus-4.5",
|
|
185
192
|
"gpt-5.5",
|
|
186
193
|
"gpt-5.4",
|
|
187
|
-
"
|
|
194
|
+
"workers-ai/@cf/moonshotai/kimi-k2.7-code",
|
|
195
|
+
"workers-ai/@cf/deepseek-ai/deepseek-v4-pro-0813",
|
|
196
|
+
"workers-ai/@cf/zai-org/glm-5.3",
|
|
197
|
+
"workers-ai/@cf/zai-org/glm-5.2",
|
|
188
198
|
"workers-ai/@cf/moonshotai/kimi-k2.6",
|
|
189
199
|
"workers-ai/@cf/nvidia/nemotron-3-120b-a12b",
|
|
200
|
+
"workers-ai/@cf/openai/gpt-oss-120b",
|
|
190
201
|
"workers-ai/@cf/zai-org/glm-4.7-flash",
|
|
191
202
|
],
|
|
192
203
|
"cloudflare-workers-ai": [
|
|
193
204
|
"@cf/moonshotai/kimi-k2.7-code",
|
|
205
|
+
"@cf/deepseek-ai/deepseek-v4-pro-0813",
|
|
206
|
+
"@cf/zai-org/glm-5.3",
|
|
194
207
|
"@cf/zai-org/glm-5.2",
|
|
195
208
|
"@cf/moonshotai/kimi-k2.6",
|
|
196
209
|
"@cf/nvidia/nemotron-3-120b-a12b",
|
|
197
210
|
"@cf/openai/gpt-oss-120b",
|
|
211
|
+
"@cf/qwen/qwen3.8-27b",
|
|
198
212
|
"@cf/qwen/qwen3-30b-a3b-fp8",
|
|
199
213
|
"@cf/zai-org/glm-4.7-flash",
|
|
200
214
|
],
|
|
201
215
|
deepseek: ["deepseek-v4-pro", "deepseek-v4-flash"],
|
|
202
216
|
fireworks: [
|
|
217
|
+
"accounts/fireworks/models/kimi-k3",
|
|
218
|
+
"accounts/fireworks/routers/kimi-k3-fast",
|
|
203
219
|
"accounts/fireworks/models/deepseek-v4-pro",
|
|
204
220
|
"accounts/fireworks/models/kimi-k2p7-code",
|
|
205
|
-
"accounts/fireworks/routers/kimi-k2p7-code-fast",
|
|
206
221
|
"accounts/fireworks/models/glm-5p2",
|
|
207
222
|
"accounts/fireworks/routers/glm-5p2-fast",
|
|
208
223
|
"accounts/fireworks/models/minimax-m3",
|
|
209
224
|
"accounts/fireworks/models/kimi-k2p6",
|
|
210
|
-
"accounts/fireworks/routers/kimi-k2p6-turbo",
|
|
211
|
-
"accounts/fireworks/routers/kimi-k2p6-fast",
|
|
212
|
-
"accounts/fireworks/models/minimax-m2p7",
|
|
213
225
|
"accounts/fireworks/models/qwen3p7-plus",
|
|
214
226
|
"accounts/fireworks/models/gpt-oss-120b",
|
|
215
|
-
"accounts/fireworks/models/gpt-oss-20b",
|
|
216
227
|
],
|
|
217
228
|
"github-copilot": [
|
|
218
229
|
"claude-opus-5",
|
|
@@ -221,6 +232,9 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
221
232
|
"claude-opus-4.7",
|
|
222
233
|
"claude-opus-4.6",
|
|
223
234
|
"claude-opus-4.5",
|
|
235
|
+
"gpt-5.6-sol",
|
|
236
|
+
"gpt-5.6-terra",
|
|
237
|
+
"gpt-5.6-luna",
|
|
224
238
|
"gpt-5.5",
|
|
225
239
|
"gpt-5.4",
|
|
226
240
|
"gpt-5.3-codex",
|
|
@@ -232,6 +246,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
232
246
|
"claude-sonnet-4.6",
|
|
233
247
|
"claude-sonnet-4.5",
|
|
234
248
|
"gpt-5-mini",
|
|
249
|
+
"kimi-k3",
|
|
235
250
|
"kimi-k2.7-code",
|
|
236
251
|
],
|
|
237
252
|
google: [
|
|
@@ -260,7 +275,10 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
260
275
|
"openai/gpt-oss-20b",
|
|
261
276
|
],
|
|
262
277
|
huggingface: [
|
|
278
|
+
"moonshotai/Kimi-K3",
|
|
263
279
|
"zai-org/GLM-5.1",
|
|
280
|
+
"zai-org/GLM-5.3-Flash",
|
|
281
|
+
"deepseek-ai/DeepSeek-V4-Pro-0813",
|
|
264
282
|
"deepseek-ai/DeepSeek-V4-Pro",
|
|
265
283
|
"moonshotai/Kimi-K2.6",
|
|
266
284
|
"MiniMaxAI/MiniMax-M2.7",
|
|
@@ -285,6 +303,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
285
303
|
"mistral-medium-2508",
|
|
286
304
|
"mistral-small-2603",
|
|
287
305
|
"devstral-2512",
|
|
306
|
+
"zai-glm-5-2",
|
|
288
307
|
],
|
|
289
308
|
openai: [
|
|
290
309
|
"gpt-5.6-sol",
|
|
@@ -334,6 +353,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
334
353
|
"glm-5.2",
|
|
335
354
|
"glm-5.1",
|
|
336
355
|
"glm-5",
|
|
356
|
+
"kimi-k3",
|
|
337
357
|
"kimi-k2.7-code",
|
|
338
358
|
"kimi-k2.6",
|
|
339
359
|
"kimi-k2.5",
|
|
@@ -344,6 +364,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
344
364
|
],
|
|
345
365
|
"opencode-go": [
|
|
346
366
|
"deepseek-v4-pro",
|
|
367
|
+
"glm-5.3",
|
|
347
368
|
"glm-5.2",
|
|
348
369
|
"glm-5.1",
|
|
349
370
|
"qwen3.7-max",
|
|
@@ -353,6 +374,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
353
374
|
"mimo-v2.5",
|
|
354
375
|
"minimax-m3",
|
|
355
376
|
"minimax-m2.7",
|
|
377
|
+
"kimi-k3",
|
|
356
378
|
"kimi-k2.7-code",
|
|
357
379
|
"kimi-k2.6",
|
|
358
380
|
],
|
|
@@ -381,6 +403,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
381
403
|
"~google/gemini-pro-latest",
|
|
382
404
|
"google/gemini-3.5-flash",
|
|
383
405
|
"google/gemini-2.5-pro",
|
|
406
|
+
"moonshotai/kimi-k3",
|
|
384
407
|
"moonshotai/kimi-k2.7-code",
|
|
385
408
|
"~moonshotai/kimi-latest",
|
|
386
409
|
"moonshotai/kimi-k2.6",
|
|
@@ -393,6 +416,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
393
416
|
"minimax/minimax-m3",
|
|
394
417
|
"minimax/minimax-m2.7",
|
|
395
418
|
"minimax/minimax-m2.1",
|
|
419
|
+
"z-ai/glm-5.3",
|
|
396
420
|
"z-ai/glm-5.2",
|
|
397
421
|
"z-ai/glm-5.1",
|
|
398
422
|
],
|
|
@@ -427,6 +451,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
427
451
|
],
|
|
428
452
|
together: [
|
|
429
453
|
"deepseek-ai/DeepSeek-V4-Pro",
|
|
454
|
+
"moonshotai/Kimi-K3",
|
|
455
|
+
"zai-org/GLM-5.3-Flash",
|
|
430
456
|
"zai-org/GLM-5.2",
|
|
431
457
|
"moonshotai/Kimi-K2.7-Code",
|
|
432
458
|
"moonshotai/Kimi-K2.6",
|
|
@@ -459,6 +485,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
459
485
|
"openai/gpt-5.2-codex",
|
|
460
486
|
"openai/gpt-5.1-codex",
|
|
461
487
|
"openai/gpt-5-codex",
|
|
488
|
+
"moonshotai/kimi-k3",
|
|
462
489
|
"moonshotai/kimi-k2.7-code",
|
|
463
490
|
"moonshotai/kimi-k2.7-code-highspeed",
|
|
464
491
|
"moonshotai/kimi-k2.6",
|
|
@@ -476,6 +503,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
476
503
|
"xai/grok-4.5",
|
|
477
504
|
"xai/grok-4.20-reasoning",
|
|
478
505
|
"minimax/minimax-m3",
|
|
506
|
+
"zai/glm-5.3",
|
|
479
507
|
"zai/glm-5.2",
|
|
480
508
|
"zai/glm-5.1",
|
|
481
509
|
],
|
|
@@ -485,15 +513,16 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
485
513
|
"grok-4.3",
|
|
486
514
|
"grok-build-0.1",
|
|
487
515
|
],
|
|
488
|
-
xiaomi: ["mimo-v2.5-pro", "mimo-v2.5
|
|
489
|
-
"xiaomi-token-plan-ams": ["mimo-v2.5-pro", "mimo-v2.5"
|
|
490
|
-
"xiaomi-token-plan-cn": ["mimo-v2.5-pro", "mimo-v2.5"
|
|
491
|
-
"xiaomi-token-plan-sgp": ["mimo-v2.5-pro", "mimo-v2.5"
|
|
516
|
+
xiaomi: ["mimo-v2.5-pro-ultraspeed", "mimo-v2.5-pro", "mimo-v2.5"],
|
|
517
|
+
"xiaomi-token-plan-ams": ["mimo-v2.5-pro", "mimo-v2.5"],
|
|
518
|
+
"xiaomi-token-plan-cn": ["mimo-v2.5-pro", "mimo-v2.5"],
|
|
519
|
+
"xiaomi-token-plan-sgp": ["mimo-v2.5-pro", "mimo-v2.5"],
|
|
492
520
|
nvidia: [
|
|
493
521
|
"nvidia/nemotron-3-ultra-550b-a55b",
|
|
494
522
|
"nvidia/nemotron-3-super-120b-a12b",
|
|
523
|
+
"moonshotai/kimi-k3",
|
|
524
|
+
"deepseek-ai/deepseek-v4-pro-0813",
|
|
495
525
|
"moonshotai/kimi-k2.6",
|
|
496
|
-
"z-ai/glm-5.2",
|
|
497
526
|
"minimaxai/minimax-m3",
|
|
498
527
|
"openai/gpt-oss-120b",
|
|
499
528
|
"nvidia/nemotron-3-nano-omni-30b-a3b-reasoning",
|
|
@@ -512,8 +541,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
512
541
|
"glm-5",
|
|
513
542
|
"glm-4.7",
|
|
514
543
|
],
|
|
515
|
-
moonshotai: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2-thinking-turbo", "kimi-k2-thinking", "kimi-k2.5"],
|
|
516
|
-
"moonshotai-cn": ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2-thinking-turbo", "kimi-k2-thinking", "kimi-k2.5"],
|
|
544
|
+
moonshotai: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2-thinking-turbo", "kimi-k2-thinking", "kimi-k2.5"],
|
|
545
|
+
"moonshotai-cn": ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2-thinking-turbo", "kimi-k2-thinking", "kimi-k2.5"],
|
|
517
546
|
};
|
|
518
547
|
|
|
519
548
|
const ORACLE_SYSTEM_PROMPT = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.84.
|
|
1
|
+
0.84.4
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# permission-gate
|
|
2
2
|
|
|
3
|
-
A small pi extension that prompts for confirmation before running potentially dangerous bash commands or writing to protected paths.
|
|
3
|
+
A small pi extension that prompts for confirmation before running potentially dangerous bash or PowerShell commands or writing to protected paths.
|
|
4
4
|
|
|
5
|
-
This
|
|
5
|
+
This started from the original `permission-gate.ts` example in [`earendil-works/pi`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/permission-gate.ts) and adds conservative shell and path hardening.
|
|
6
6
|
|
|
7
7
|
## What it checks
|
|
8
8
|
|
|
9
9
|
- `rm -rf`
|
|
10
|
+
- PowerShell `Remove-Item -Recurse -Force` and its standard aliases, including module-qualified, interpolated, splatted, and backtick-escaped forms
|
|
10
11
|
- `sudo`
|
|
11
12
|
- `chmod` / `chown` with `777`
|
|
12
13
|
- direct `write` / `edit` calls touching normalized protected paths:
|
|
@@ -18,6 +19,12 @@ Safe `.env` templates/examples such as `.env.example` and `.env.production.templ
|
|
|
18
19
|
|
|
19
20
|
If pi is running without an interactive UI, it blocks matching commands and protected path writes by default.
|
|
20
21
|
|
|
22
|
+
For Pi's built-in local Windows PowerShell tool, commands that pass the fast lexical checks are also parsed with the same PowerShell installation's AST parser before execution. Malformed input, parser failures, `Remove-Item` parameter splatting, command-resolution changes (aliases/modules/providers/direct function or filter definitions), known script/process/job/member invocation wrappers, computed member calls, and analyzer inputs above 16,000 UTF-8 bytes are treated conservatively and require confirmation (or are blocked when no UI is available). Computed targets passed to provider-capable mutation commands also require confirmation because the target could resolve to `Alias:` or `Function:`. Literal quoted/commented examples, including literal here-strings, stay benign. Definitely enabled `-WhatIf` removals (`-WhatIf`, `-WhatIf:$true`, or `-WhatIf:1`) remain non-destructive and are allowed; computed switch values require confirmation.
|
|
23
|
+
|
|
24
|
+
Non-Windows and custom/remote `powershell` tools receive the conservative lexical checks, but only Pi's built-in local Windows tool can be verified with its actual parser. Pi extensions are trusted code: tool overrides, remote operations, spawn hooks, and mutations made by handlers loaded after this gate are outside its boundary and must enforce their own final-input policy.
|
|
25
|
+
|
|
26
|
+
This is a targeted confirmation guard for the command classes listed above, not a shell sandbox or a proof that every unlisted executable/API is harmless.
|
|
27
|
+
|
|
21
28
|
## Install
|
|
22
29
|
|
|
23
30
|
### Standalone npm package
|
|
@@ -47,6 +54,7 @@ Then reload pi:
|
|
|
47
54
|
## Notes
|
|
48
55
|
|
|
49
56
|
- Hooks the `tool_call` event.
|
|
50
|
-
- Inspects `bash`, `write`, and `edit` tool calls.
|
|
57
|
+
- Inspects `bash`, `powershell`, `write`, and `edit` tool calls.
|
|
58
|
+
- Validates shell timeouts as well as command strings and fails closed on malformed calls.
|
|
51
59
|
- Normalizes relative/absolute paths before matching so traversal tricks do not bypass the guard.
|
|
52
60
|
- Prompts with a simple `Yes` / `No` selector before allowing dangerous commands or protected path writes.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Permission Gate Extension
|
|
3
3
|
*
|
|
4
|
-
* Prompts for confirmation before running potentially dangerous bash
|
|
5
|
-
* or modifying protected paths via write/edit.
|
|
4
|
+
* Prompts for confirmation before running potentially dangerous bash or
|
|
5
|
+
* PowerShell commands, or modifying protected paths via write/edit.
|
|
6
6
|
* Protected paths include exact .git and node_modules path segments plus
|
|
7
7
|
* secret-bearing .env files (excluding example/template variants).
|
|
8
8
|
*/
|
|
@@ -11,10 +11,19 @@ import * as path from "node:path";
|
|
|
11
11
|
|
|
12
12
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
13
13
|
|
|
14
|
+
import {
|
|
15
|
+
analyzePowerShellLexically,
|
|
16
|
+
buildPowerShellAstCommand,
|
|
17
|
+
MAX_POWERSHELL_ANALYSIS_SOURCE_BYTES,
|
|
18
|
+
parsePowerShellAstOutput,
|
|
19
|
+
} from "./powershell-safety.ts";
|
|
20
|
+
|
|
14
21
|
const dangerousPatterns = [/\bsudo\b/i, /\b(chmod|chown)\b.*777/i];
|
|
15
22
|
const protectedPathSegments = new Set([".git", "node_modules"]);
|
|
16
23
|
const shellCommandSeparators = new Set([";", "&", "&&", "||", "|", "\n", "(", ")", "{", "}"]);
|
|
17
24
|
const safeEnvSuffixes = new Set(["example", "examples", "template", "templates"]);
|
|
25
|
+
const MAX_SHELL_TIMEOUT_SECONDS = 2_147_483_647 / 1000;
|
|
26
|
+
const POWERSHELL_ANALYSIS_TIMEOUT_MS = 5_000;
|
|
18
27
|
|
|
19
28
|
type GuardDecision = { block: true; reason: string } | undefined;
|
|
20
29
|
type NormalizedPath = {
|
|
@@ -30,6 +39,7 @@ type ShellToken = {
|
|
|
30
39
|
|
|
31
40
|
type WriteInput = { path: string; content: string };
|
|
32
41
|
type EditInput = { path: string; edits: Array<{ oldText: string; newText: string }> };
|
|
42
|
+
type ShellInput = { command: string; timeout?: number };
|
|
33
43
|
|
|
34
44
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
35
45
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -524,6 +534,55 @@ function hasDangerousRecursiveRm(command: string): boolean {
|
|
|
524
534
|
return evaluateTokens(currentCommand);
|
|
525
535
|
}
|
|
526
536
|
|
|
537
|
+
function validateShellInput(input: unknown): ShellInput | undefined {
|
|
538
|
+
if (!isRecord(input) || typeof input.command !== "string" || input.command.trim() === "") return undefined;
|
|
539
|
+
if (
|
|
540
|
+
input.timeout !== undefined &&
|
|
541
|
+
(typeof input.timeout !== "number" ||
|
|
542
|
+
!Number.isFinite(input.timeout) ||
|
|
543
|
+
input.timeout <= 0 ||
|
|
544
|
+
input.timeout > MAX_SHELL_TIMEOUT_SECONDS)
|
|
545
|
+
) {
|
|
546
|
+
return undefined;
|
|
547
|
+
}
|
|
548
|
+
return input.timeout === undefined
|
|
549
|
+
? { command: input.command }
|
|
550
|
+
: { command: input.command, timeout: input.timeout };
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async function hasDangerousPowerShellCommand(
|
|
554
|
+
pi: ExtensionAPI,
|
|
555
|
+
command: string,
|
|
556
|
+
signal?: AbortSignal,
|
|
557
|
+
): Promise<boolean> {
|
|
558
|
+
// Keep both lexical work and Windows' base64-expanded -Command transport
|
|
559
|
+
// bounded below the platform command-line limit. Oversized input is
|
|
560
|
+
// deliberately treated as requiring confirmation rather than failing open.
|
|
561
|
+
if (Buffer.byteLength(command, "utf8") > MAX_POWERSHELL_ANALYSIS_SOURCE_BYTES) return true;
|
|
562
|
+
|
|
563
|
+
const lexicalAnalysis = analyzePowerShellLexically(command);
|
|
564
|
+
if (lexicalAnalysis.risky) return true;
|
|
565
|
+
|
|
566
|
+
// Pi only exposes its built-in PowerShell tool on Windows. On that platform,
|
|
567
|
+
// ask PowerShell's own parser for the final word so ordinary syntax features
|
|
568
|
+
// (module qualification, interpolation, splatting, and backtick escapes) cannot
|
|
569
|
+
// bypass the fallback lexer. Parser failures and malformed output fail closed.
|
|
570
|
+
if (process.platform !== "win32") return false;
|
|
571
|
+
|
|
572
|
+
try {
|
|
573
|
+
const { getPowerShellConfig } = await import("@earendil-works/pi-coding-agent");
|
|
574
|
+
const config = getPowerShellConfig();
|
|
575
|
+
const result = await pi.exec(config.shell, [...config.args, buildPowerShellAstCommand(command)], {
|
|
576
|
+
timeout: POWERSHELL_ANALYSIS_TIMEOUT_MS,
|
|
577
|
+
signal,
|
|
578
|
+
});
|
|
579
|
+
if (result.code !== 0 || result.killed) return true;
|
|
580
|
+
return parsePowerShellAstOutput(result.stdout).risky;
|
|
581
|
+
} catch {
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
527
586
|
function validateWriteInput(input: unknown): WriteInput | undefined {
|
|
528
587
|
if (!isRecord(input)) return undefined;
|
|
529
588
|
if (typeof input.path !== "string" || typeof input.content !== "string") return undefined;
|
|
@@ -576,13 +635,17 @@ async function confirmProtectedPathAction(
|
|
|
576
635
|
|
|
577
636
|
export default function (pi: ExtensionAPI) {
|
|
578
637
|
pi.on("tool_call", async (event, ctx) => {
|
|
579
|
-
if (event.toolName === "bash") {
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
638
|
+
if (event.toolName === "bash" || event.toolName === "powershell") {
|
|
639
|
+
const shellToolName = event.toolName;
|
|
640
|
+
const input = validateShellInput(event.input);
|
|
641
|
+
if (!input) {
|
|
642
|
+
return { block: true, reason: `Malformed ${shellToolName} command blocked` };
|
|
583
643
|
}
|
|
644
|
+
const { command } = input;
|
|
584
645
|
|
|
585
|
-
|
|
646
|
+
const isDangerous = shellToolName === "powershell"
|
|
647
|
+
? await hasDangerousPowerShellCommand(pi, command, ctx.signal)
|
|
648
|
+
: hasDangerousRecursiveRm(command) || dangerousPatterns.some((pattern) => pattern.test(command));
|
|
586
649
|
|
|
587
650
|
if (isDangerous) {
|
|
588
651
|
if (!ctx.hasUI) {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-permission-gate",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "A pi extension that prompts before dangerous
|
|
3
|
+
"version": "0.1.14",
|
|
4
|
+
"description": "A pi extension that prompts before dangerous shell commands and protected file writes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
7
7
|
"pi",
|
|
8
8
|
"security",
|
|
9
9
|
"bash",
|
|
10
|
+
"powershell",
|
|
10
11
|
"permissions",
|
|
11
12
|
"file-safety"
|
|
12
13
|
],
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
20
21
|
"index.ts",
|
|
22
|
+
"powershell-safety.ts",
|
|
21
23
|
"README.md",
|
|
22
24
|
".pi-fleet-tested-version"
|
|
23
25
|
],
|