refactor(core): simplify search root protocol (#31060)
This commit is contained in:
parent
4ac4df448a
commit
09d9cf01f9
8 changed files with 18 additions and 89 deletions
|
|
@ -153,17 +153,13 @@ export class ListTarget extends Schema.Class<ListTarget>("FileSystem.ListTarget"
|
|||
resource: Schema.String,
|
||||
}) {}
|
||||
|
||||
/** Canonical read authority for Location-scoped search and metadata leaves. */
|
||||
/** Canonical root and permission resource for Location-scoped search. */
|
||||
export class RootTarget extends Schema.Class<RootTarget>("FileSystem.RootTarget")({
|
||||
absolute: Schema.String,
|
||||
real: Schema.String,
|
||||
directory: Schema.String,
|
||||
root: Schema.String,
|
||||
resource: Schema.String,
|
||||
reference: Schema.NonEmptyString.pipe(Schema.optional),
|
||||
type: Schema.Literals(["file", "directory"]),
|
||||
dev: Schema.Number,
|
||||
ino: Schema.Number.pipe(Schema.optional),
|
||||
}) {}
|
||||
|
||||
export class Entry extends Schema.Class<Entry>("FileSystem.Entry")({
|
||||
|
|
@ -221,9 +217,8 @@ export interface Interface {
|
|||
readonly resolveReadPath: (input: ReadInput) => Effect.Effect<ReadPath>
|
||||
readonly readTool: (input: ReadInput, page?: TextPageInput) => Effect.Effect<Content | TextPage>
|
||||
readonly list: (input?: ListInput) => Effect.Effect<Entry[]>
|
||||
/** Select a contained canonical read root without asserting leaf policy. */
|
||||
/** Resolve a contained canonical search root and its permission resource. */
|
||||
readonly resolveRoot: (input?: ListInput) => Effect.Effect<RootTarget>
|
||||
readonly revalidateRoot: (target: RootTarget) => Effect.Effect<RootTarget>
|
||||
readonly resolveList: (input?: ListInput) => Effect.Effect<ListTarget>
|
||||
readonly listResolved: (target: ListTarget) => Effect.Effect<Entry[]>
|
||||
readonly listPage: (input?: ListPageInput) => Effect.Effect<ListPage>
|
||||
|
|
@ -535,22 +530,8 @@ export const layer = Layer.effect(
|
|||
resource: input.reference === undefined ? relative : `${input.reference}:${relative}`,
|
||||
reference: input.reference,
|
||||
type,
|
||||
dev: info.dev,
|
||||
ino: Option.getOrUndefined(info.ino),
|
||||
})
|
||||
})
|
||||
const revalidateRoot = Effect.fn("FileSystem.revalidateRoot")(function* (target: RootTarget) {
|
||||
const canonical = yield* fs.realPath(target.absolute).pipe(Effect.orDie)
|
||||
if (canonical !== target.real) return yield* Effect.die(new Error("Search root changed after approval"))
|
||||
const info = yield* fs.stat(canonical).pipe(Effect.orDie)
|
||||
if (
|
||||
info.type !== (target.type === "file" ? "File" : "Directory") ||
|
||||
info.dev !== target.dev ||
|
||||
Option.getOrUndefined(info.ino) !== target.ino
|
||||
)
|
||||
return yield* Effect.die(new Error("Search root identity changed after approval"))
|
||||
return target
|
||||
})
|
||||
const listResolved = Effect.fn("FileSystem.listResolved")(function* (directory: ListTarget) {
|
||||
return yield* fs.readDirectoryEntries(directory.real).pipe(
|
||||
Effect.orDie,
|
||||
|
|
@ -613,7 +594,6 @@ export const layer = Layer.effect(
|
|||
return yield* listResolved(yield* resolveList(input))
|
||||
}),
|
||||
resolveRoot,
|
||||
revalidateRoot,
|
||||
resolveList,
|
||||
listResolved,
|
||||
listPage: Effect.fn("FileSystem.listPage")(function* (input) {
|
||||
|
|
|
|||
|
|
@ -24,14 +24,9 @@ export const MAX_LINE_PREVIEW_LENGTH = 2_000
|
|||
|
||||
export const ResultLimit = PositiveInt.check(Schema.isLessThanOrEqualTo(MAX_RESULT_LIMIT))
|
||||
|
||||
const RootInput = {
|
||||
path: Schema.String.pipe(Schema.optional),
|
||||
reference: Schema.NonEmptyString.pipe(Schema.optional),
|
||||
}
|
||||
|
||||
export const FilesInput = Schema.Struct({
|
||||
pattern: Schema.String,
|
||||
...RootInput,
|
||||
...FileSystem.ListInput.fields,
|
||||
limit: ResultLimit.pipe(Schema.optional),
|
||||
})
|
||||
export type FilesInput = typeof FilesInput.Type & { readonly signal?: AbortSignal }
|
||||
|
|
@ -39,7 +34,7 @@ export type FilesInput = typeof FilesInput.Type & { readonly signal?: AbortSigna
|
|||
export const GrepInput = Schema.Struct({
|
||||
pattern: Schema.String,
|
||||
include: Schema.String.pipe(Schema.optional),
|
||||
...RootInput,
|
||||
...FileSystem.ListInput.fields,
|
||||
limit: ResultLimit.pipe(Schema.optional),
|
||||
})
|
||||
export type GrepInput = typeof GrepInput.Type & { readonly signal?: AbortSignal }
|
||||
|
|
@ -82,11 +77,8 @@ export class GrepResult extends Schema.Class<GrepResult>("LocationSearch.GrepRes
|
|||
}) {}
|
||||
|
||||
export interface Interface {
|
||||
readonly files: (input: FilesInput, root?: FileSystem.RootTarget) => Effect.Effect<FilesResult, Ripgrep.Error>
|
||||
readonly grep: (
|
||||
input: GrepInput,
|
||||
root?: FileSystem.RootTarget,
|
||||
) => Effect.Effect<GrepResult, Ripgrep.Error | Ripgrep.InvalidPatternError>
|
||||
readonly files: (input: FilesInput) => Effect.Effect<FilesResult, Ripgrep.Error>
|
||||
readonly grep: (input: GrepInput) => Effect.Effect<GrepResult, Ripgrep.Error | Ripgrep.InvalidPatternError>
|
||||
}
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/LocationSearch") {}
|
||||
|
|
@ -123,8 +115,8 @@ export const layer = Layer.effect(
|
|||
})
|
||||
|
||||
return Service.of({
|
||||
files: Effect.fn("LocationSearch.files")(function* (input, approvedRoot) {
|
||||
const root = yield* filesystem.revalidateRoot(approvedRoot ?? (yield* filesystem.resolveRoot(input)))
|
||||
files: Effect.fn("LocationSearch.files")(function* (input) {
|
||||
const root = yield* filesystem.resolveRoot(input)
|
||||
if (root.type !== "directory")
|
||||
return yield* Effect.die(new globalThis.Error("Files search path must be a directory"))
|
||||
const result = yield* ripgrep.files({
|
||||
|
|
@ -145,8 +137,8 @@ export const layer = Layer.effect(
|
|||
partial: result.partial || items.length !== result.items.length,
|
||||
})
|
||||
}),
|
||||
grep: Effect.fn("LocationSearch.grep")(function* (input, approvedRoot) {
|
||||
const root = yield* filesystem.revalidateRoot(approvedRoot ?? (yield* filesystem.resolveRoot(input)))
|
||||
grep: Effect.fn("LocationSearch.grep")(function* (input) {
|
||||
const root = yield* filesystem.resolveRoot(input)
|
||||
const cwd = root.type === "directory" ? root.real : path.dirname(root.real)
|
||||
const result = yield* ripgrep.grep({
|
||||
cwd,
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ const definition = Tool.make({
|
|||
})
|
||||
|
||||
/**
|
||||
* Location-scoped glob leaf. FileSystem selects a canonical root for
|
||||
* permission metadata; LocationSearch owns containment and traversal.
|
||||
* Location-scoped glob leaf. FileSystem supplies canonical permission metadata;
|
||||
* LocationSearch resolves the current root and owns containment and traversal.
|
||||
*
|
||||
* TODO: Revisit root-specific search permission resources if named-reference policy needs independent allow/deny rules.
|
||||
*/
|
||||
|
|
@ -73,7 +73,7 @@ export const layer = Layer.effectDiscard(
|
|||
limit: parameters.limit,
|
||||
},
|
||||
})
|
||||
return yield* search.files(parameters, root)
|
||||
return yield* search.files(parameters)
|
||||
}).pipe(
|
||||
Effect.catchCause((cause) =>
|
||||
Effect.fail(
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ const definition = Tool.make({
|
|||
})
|
||||
|
||||
/**
|
||||
* Location-scoped grep leaf. FileSystem selects a canonical root for
|
||||
* permission metadata; LocationSearch owns containment and ripgrep execution.
|
||||
* Location-scoped grep leaf. FileSystem supplies canonical permission metadata;
|
||||
* LocationSearch resolves the current root and owns containment and ripgrep execution.
|
||||
*
|
||||
* TODO: Revisit root-specific search permission resources if named-reference policy needs independent allow/deny rules.
|
||||
*/
|
||||
|
|
@ -89,7 +89,7 @@ export const layer = Layer.effectDiscard(
|
|||
limit: parameters.limit,
|
||||
},
|
||||
})
|
||||
return yield* search.grep(parameters, root)
|
||||
return yield* search.grep(parameters)
|
||||
}).pipe(
|
||||
Effect.catchCause((cause) => {
|
||||
const error = Cause.squash(cause)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue