fix permission config order (#24222)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
export * as ConfigPermission from "./permission"
|
||||
import { Schema, SchemaGetter } from "effect"
|
||||
import { zod } from "@/util/effect-zod"
|
||||
import z from "zod"
|
||||
import { ZodOverride, zod } from "@/util/effect-zod"
|
||||
import { withStatics } from "@/util/schema"
|
||||
|
||||
export const Action = Schema.Literals(["ask", "allow", "deny"])
|
||||
@@ -18,17 +19,9 @@ export const Rule = Schema.Union([Action, Object])
|
||||
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
||||
export type Rule = Schema.Schema.Type<typeof Rule>
|
||||
|
||||
// Known permission keys get explicit types — most are full Rule (either a
|
||||
// single Action or a per-pattern object), but a handful of tools take no
|
||||
// sub-target patterns and are Action-only. Unknown keys fall through the
|
||||
// Record rest signature as Rule.
|
||||
//
|
||||
// StructWithRest canonicalises key order on decode (known first, then rest),
|
||||
// which used to require the `__originalKeys` preprocess hack because
|
||||
// `Permission.fromConfig` depended on the user's insertion order. That
|
||||
// dependency is gone — `fromConfig` now sorts top-level keys so wildcard
|
||||
// permissions come before specifics, making the final precedence
|
||||
// order-independent.
|
||||
// Known permission keys get explicit types in the Effect schema for generated
|
||||
// docs/types. Runtime config parsing uses `InfoZod` below so user key order is
|
||||
// preserved for permission precedence.
|
||||
const InputObject = Schema.StructWithRest(
|
||||
Schema.Struct({
|
||||
read: Schema.optional(Rule),
|
||||
@@ -60,6 +53,18 @@ const InputSchema = Schema.Union([Action, InputObject])
|
||||
const normalizeInput = (input: Schema.Schema.Type<typeof InputSchema>): Schema.Schema.Type<typeof InputObject> =>
|
||||
typeof input === "string" ? { "*": input } : input
|
||||
|
||||
const ACTION_ONLY = new Set(["todowrite", "question", "webfetch", "websearch", "codesearch", "doom_loop"])
|
||||
|
||||
const InfoZod = z
|
||||
.union([zod(Action), z.record(z.string(), z.union([zod(Action), z.record(z.string(), zod(Action))]))])
|
||||
.transform(normalizeInput)
|
||||
.superRefine((input, ctx) => {
|
||||
for (const [key, value] of globalThis.Object.entries(input)) {
|
||||
if (!ACTION_ONLY.has(key) || typeof value === "string") continue
|
||||
ctx.addIssue({ code: "custom", message: `${key} must be a permission action`, path: [key] })
|
||||
}
|
||||
})
|
||||
|
||||
export const Info = InputSchema.pipe(
|
||||
Schema.decodeTo(InputObject, {
|
||||
decode: SchemaGetter.transform(normalizeInput),
|
||||
@@ -70,6 +75,7 @@ export const Info = InputSchema.pipe(
|
||||
}),
|
||||
)
|
||||
.annotate({ identifier: "PermissionConfig" })
|
||||
.annotate({ [ZodOverride]: InfoZod })
|
||||
.pipe(
|
||||
// Walker already emits the decodeTo transform into the derived zod (see
|
||||
// `encoded()` in effect-zod.ts), so just expose that directly.
|
||||
|
||||
Reference in New Issue
Block a user