fix(pi-permission-inner-cmd): unwrap timeout in real-world command forms

- accept GNU timeout durations without a unit suffix and with decimals (timeout 240 …)
- detect the wrapper on details.command and strip it from the full command so scaffolded inputs (cd … && timeout … | tail) unwrap
- re-evaluate the full de-wrapped compound so sibling commands cannot hide behind the wrapper allow
- defer fail-closed when the unit is not a unique substring of the full command
- amend ADR 0001 with the relaxed grammar and the scaffolded-command handling
This commit is contained in:
2026-08-12 11:20:46 +08:00
parent 43ae2db90b
commit 6008c9e817
5 changed files with 240 additions and 56 deletions
@@ -6,7 +6,7 @@ import {
} from "../src/recognizer";
describe("parseTimeoutWrapper", () => {
it("matches the strict simple-timeout form", () => {
it("matches the simple-timeout form", () => {
expect(parseTimeoutWrapper("timeout 30s pnpm test")).toEqual({
duration: "30s",
innerCommand: "pnpm test",
@@ -25,6 +25,21 @@ describe("parseTimeoutWrapper", () => {
});
});
it("accepts GNU durations: bare integer (seconds) and decimals", () => {
expect(parseTimeoutWrapper("timeout 240 pnpm test")).toEqual({
duration: "240",
innerCommand: "pnpm test",
});
expect(parseTimeoutWrapper("timeout 1.5h deploy")).toEqual({
duration: "1.5h",
innerCommand: "deploy",
});
expect(parseTimeoutWrapper("timeout 2.5s build")).toEqual({
duration: "2.5s",
innerCommand: "build",
});
});
it("preserves compound inner programs as the inner command", () => {
expect(parseTimeoutWrapper("timeout 60s pnpm test && git push")).toEqual({
duration: "60s",
@@ -47,8 +62,10 @@ describe("parseTimeoutWrapper", () => {
});
});
it("rejects leading-zero and multi-letter durations", () => {
it("rejects zero, leading-zero, ms, and multi-letter durations", () => {
expect(parseTimeoutWrapper("timeout 0 pnpm test")).toBeUndefined();
expect(parseTimeoutWrapper("timeout 0s pnpm test")).toBeUndefined();
expect(parseTimeoutWrapper("timeout 0.5s pnpm test")).toBeUndefined();
expect(parseTimeoutWrapper("timeout 030s pnpm test")).toBeUndefined();
expect(parseTimeoutWrapper("timeout 30ms pnpm test")).toBeUndefined();
expect(parseTimeoutWrapper("timeout 30sec pnpm test")).toBeUndefined();