diff --git a/Cargo.lock b/Cargo.lock index 17a80d7335a..2032e26e44a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4633,43 +4633,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "grep-matcher" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9417543f4870fc8f1c8e1af870afae2431007626d9e703fce6471c468d33847" -dependencies = [ - "memchr", -] - -[[package]] -name = "grep-regex" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce0c256c3ad82bcc07b812c15a45ec1d398122e8e15124f96695234db7112ef" -dependencies = [ - "bstr", - "grep-matcher", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "grep-searcher" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72348823a0eafc4bc2e9051064f28b5b42cc100b571b3a35d67918d711efcbc6" -dependencies = [ - "bstr", - "encoding_rs", - "encoding_rs_io", - "grep-matcher", - "log", - "memchr", - "memmap2", -] - [[package]] name = "group" version = "0.14.0" @@ -7002,9 +6965,6 @@ dependencies = [ "encoding_rs", "flate2", "gix", - "glob", - "grep-regex", - "grep-searcher", "ignore", "imagesize 0.15.0", "libc", diff --git a/packages/plugin-sdk/src/contracts.ts b/packages/plugin-sdk/src/contracts.ts index a8dae8f9d24..53384a3fec9 100644 --- a/packages/plugin-sdk/src/contracts.ts +++ b/packages/plugin-sdk/src/contracts.ts @@ -45857,119 +45857,6 @@ export const contracts: { }, resources: {}, }, - glob: { - mode: 'query', - input: { - type: 'object', - additionalProperties: false, - properties: { - pattern: { - type: 'string', - minLength: 1, - }, - cwd: { - type: 'string', - minLength: 1, - default: '.', - }, - }, - required: ['pattern'], - title: 'Input', - $schema: 'https://json-schema.org/draft/2020-12/schema', - }, - output: { - oneOf: [ - { - type: 'object', - additionalProperties: false, - properties: { - kind: { - type: 'string', - const: 'completed', - }, - value: true, - }, - required: ['kind', 'value'], - }, - { - type: 'object', - additionalProperties: false, - properties: { - kind: { - type: 'string', - const: 'failed', - }, - message: { - type: 'string', - }, - }, - required: ['kind', 'message'], - description: - 'A known completed failure. Uncertain external effects use the original receipt.', - }, - ], - title: 'WorkspaceFileResult', - $schema: 'https://json-schema.org/draft/2020-12/schema', - }, - resources: {}, - }, - grep: { - mode: 'query', - input: { - type: 'object', - additionalProperties: false, - properties: { - pattern: { - type: 'string', - }, - path: { - type: 'string', - default: '.', - }, - glob: { - type: 'string', - }, - }, - required: ['pattern'], - title: 'Input', - $schema: 'https://json-schema.org/draft/2020-12/schema', - }, - output: { - oneOf: [ - { - type: 'object', - additionalProperties: false, - properties: { - kind: { - type: 'string', - const: 'completed', - }, - value: true, - }, - required: ['kind', 'value'], - }, - { - type: 'object', - additionalProperties: false, - properties: { - kind: { - type: 'string', - const: 'failed', - }, - message: { - type: 'string', - }, - }, - required: ['kind', 'message'], - description: - 'A known completed failure. Uncertain external effects use the original receipt.', - }, - ], - title: 'WorkspaceFileResult', - $schema: 'https://json-schema.org/draft/2020-12/schema', - }, - resources: {}, - }, publishChange: { mode: 'mutation', input: { diff --git a/platform/capabilities/src/filesystem.rs b/platform/capabilities/src/filesystem.rs index 353738d80f3..2defe337d49 100644 --- a/platform/capabilities/src/filesystem.rs +++ b/platform/capabilities/src/filesystem.rs @@ -72,8 +72,6 @@ pub enum Operation { Read(ReadInput), Write(Write), Edit(Edit), - Glob(Glob), - Grep(Grep), Patch(Patch), Entries(entries::Operation), } @@ -91,22 +89,6 @@ pub struct Edit { pub new_string: String, } #[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct Glob { - pub pattern: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub cwd: Option, -} -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct Grep { - pub pattern: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub path: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub glob: Option, -} -#[derive(Clone, Debug, Deserialize, Serialize)] #[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)] pub enum Patch { CreateFile { path: String, diff: String }, @@ -116,24 +98,16 @@ pub enum Patch { impl Operation { pub fn is_read(&self) -> bool { match self { - Self::Read(_) | Self::Glob(_) | Self::Grep(_) => true, + Self::Read(_) => true, Self::Entries(operation) => operation.is_read(), _ => false, } } - pub fn required_tool(&self) -> &'static str { - match self { - Self::Entries(operation) => operation.required_tool(), - _ => self.name(), - } - } pub fn name(&self) -> &'static str { match self { Self::Read(_) => "Read", Self::Write(_) => "Write", Self::Edit(_) => "Edit", - Self::Glob(_) => "Glob", - Self::Grep(_) => "Grep", Self::Patch(_) => "apply_patch", Self::Entries(operation) => operation.name(), } diff --git a/platform/capabilities/src/filesystem/entries.rs b/platform/capabilities/src/filesystem/entries.rs index 4b2c6a73eea..d90cef04da6 100644 --- a/platform/capabilities/src/filesystem/entries.rs +++ b/platform/capabilities/src/filesystem/entries.rs @@ -61,14 +61,6 @@ impl Operation { pub fn is_read(&self) -> bool { matches!(self, Self::Read(_) | Self::List(_) | Self::Stat { .. }) } - pub fn required_tool(&self) -> &'static str { - match self { - Self::Read(_) | Self::Stat { .. } => "Read", - Self::List(_) => "Glob", - Self::Write(_) | Self::CreateDirectory { .. } => "Write", - Self::Remove { .. } | Self::Rename { .. } | Self::Sync { .. } => "apply_patch", - } - } pub fn name(&self) -> &'static str { match self { Self::Read(_) => "files.read_bytes", diff --git a/platform/presentation/src/shell/project.rs b/platform/presentation/src/shell/project.rs index c0766023460..19468600059 100644 --- a/platform/presentation/src/shell/project.rs +++ b/platform/presentation/src/shell/project.rs @@ -32,7 +32,7 @@ use std::sync::Arc; const MODEL_BYTES: usize = maka_plugin_core::terminal::PREVIEW_BYTES; const TERMINAL_MARKER: &str = "[terminal snapshot truncated to fit the output limit]"; const FIELD_MARKER: &str = "[runtime resource field truncated]"; -const RECOVERY: &str = "If the command is safe to re-run, redirect its output to a file (e.g. `cmd > out.txt 2>&1`) then Read or Grep that file for the omitted portion. If re-running could repeat side effects, do not. Otherwise work from the kept output above."; +const RECOVERY: &str = "If the command is safe to re-run, redirect its output to a file (e.g. `cmd > out.txt 2>&1`) then use Read on that file for the omitted portion. If re-running could repeat side effects, do not. Otherwise work from the kept output above."; pub fn local_update(record: ShellRun) -> Result { project(record, true, None) diff --git a/plugins/agent/engine/src/history/references.rs b/plugins/agent/engine/src/history/references.rs index 27f76cdc0a5..65c491a158c 100644 --- a/plugins/agent/engine/src/history/references.rs +++ b/plugins/agent/engine/src/history/references.rs @@ -90,7 +90,7 @@ pub(super) fn text( let json = serde_json::to_string(references) .map_err(|error| RunError::Internal(error.to_string()))?; text.push_str("\n\n\n"); - text.push_str(match language { Language::En => "Live directory paths on the originating Runtime Host, accessible with Glob/Read. The project and working directory are unchanged.\n", Language::ZhCn => "原始运行时主机上的实时目录路径,可通过 Glob/Read 访问。项目与工作目录不变。\n", Language::ZhTw => "原始執行階段主機上的即時目錄路徑,可透過 Glob/Read 存取。專案與工作目錄不變。\n" }); + text.push_str(match language { Language::En => "Live directory paths on the originating Runtime Host, available to Shell and Read according to workspace permissions. The project and working directory are unchanged.\n", Language::ZhCn => "原始运行时主机上的实时目录路径,Shell 和 Read 可按工作区权限访问。项目与工作目录不变。\n", Language::ZhTw => "原始執行階段主機上的即時目錄路徑,Shell 和 Read 可依工作區權限存取。專案與工作目錄不變。\n" }); for character in json.chars() { match character { '<' => text.push_str("\\u003c"), diff --git a/plugins/agent/native/src/tools.rs b/plugins/agent/native/src/tools.rs index 4f187fe3274..e7a92f3ee09 100644 --- a/plugins/agent/native/src/tools.rs +++ b/plugins/agent/native/src/tools.rs @@ -410,8 +410,6 @@ fn catalog_inner( "write" => Some(title::Kind::Write), "edit" => Some(title::Kind::Edit), "applyPatch" => Some(title::Kind::Patch), - "glob" => Some(title::Kind::Glob), - "grep" => Some(title::Kind::Grep), _ => None, } } else if binding.contract() == &web_contract { diff --git a/plugins/agent/native/src/tools/title.rs b/plugins/agent/native/src/tools/title.rs index d2e28aafdf3..a26d14b596c 100644 --- a/plugins/agent/native/src/tools/title.rs +++ b/plugins/agent/native/src/tools/title.rs @@ -31,7 +31,6 @@ pub(crate) enum Kind { Patch, Glob, List, - Grep, Fetch, Search, CreateSession, @@ -45,7 +44,7 @@ impl Kind { let target = match self { Self::Read | Self::ReadImage | Self::Write | Self::Edit | Self::List => &input["path"], Self::Patch => &input["operation"]["path"], - Self::Glob | Self::Grep => &input["pattern"], + Self::Glob => &input["pattern"], Self::Fetch => &input["url"], Self::CreateSession | Self::ForkSession => &input["title"], Self::SendSession => &input["message"], @@ -82,7 +81,6 @@ impl Kind { Self::Patch => ("Patch file", "修补文件", "修補檔案"), Self::Glob => ("Find files", "查找文件", "尋找檔案"), Self::List => ("List directory", "列出目录", "列出目錄"), - Self::Grep => ("Search files", "搜索文件", "搜尋檔案"), Self::Fetch => ("Read web page", "读取网页", "讀取網頁"), Self::Search => ("Search the web", "搜索网络", "搜尋網路"), Self::CreateSession => ("Create conversation", "创建会话", "建立對話"), diff --git a/plugins/agent/tool-catalog/src/availability.rs b/plugins/agent/tool-catalog/src/availability.rs index fb927fe09f9..f2295cc8fad 100644 --- a/plugins/agent/tool-catalog/src/availability.rs +++ b/plugins/agent/tool-catalog/src/availability.rs @@ -277,6 +277,6 @@ fn invalid() -> ToolRejection { fn direct(name: &str) -> bool { matches!( name, - "Shell" | "Read" | "Glob" | "Grep" | "AskUserQuestion" | "ProcessStop" | "ProcessInput" + "Shell" | "Read" | "AskUserQuestion" | "ProcessStop" | "ProcessInput" ) } diff --git a/plugins/workspace/api/src/files.rs b/plugins/workspace/api/src/files.rs index 861e6f92262..52626eb18f5 100644 --- a/plugins/workspace/api/src/files.rs +++ b/plugins/workspace/api/src/files.rs @@ -42,8 +42,6 @@ pub fn method(name: &str) -> Option<(&'static str, bool)> { use maka_fs_tools::*; Some(match name { "readText" | "readImage" => (READ_NAME, false), - "glob" => (GLOB_NAME, false), - "grep" => (GREP_NAME, false), "write" => (WRITE_NAME, true), "edit" => (EDIT_NAME, true), "applyPatch" => (PATCH_NAME, true), @@ -54,8 +52,6 @@ pub fn contract() -> Contract { let mut methods: BTreeMap = [ ("readText", maka_fs_tools::read_schema()), ("readImage", maka_fs_tools::read_schema()), - ("glob", maka_fs_tools::glob_schema()), - ("grep", maka_fs_tools::grep_schema()), ("write", maka_fs_tools::write_schema()), ("edit", maka_fs_tools::edit_schema()), ("applyPatch", maka_fs_tools::patch_schema()), diff --git a/plugins/workspace/workspace-files/engine/Cargo.toml b/plugins/workspace/workspace-files/engine/Cargo.toml index 9c2c52ec2c2..2c3e484b107 100644 --- a/plugins/workspace/workspace-files/engine/Cargo.toml +++ b/plugins/workspace/workspace-files/engine/Cargo.toml @@ -35,10 +35,7 @@ cap-fs-ext.workspace = true dunce = "1.0.5" encoding_rs = "0.8.35" flate2 = { version = "1.1.10", default-features = false, features = ["rust_backend"] } -glob = "0.3.4" gix = { workspace = true, features = ["status", "worktree-mutation"] } -grep-searcher = "0.1.17" -grep-regex = "0.1.14" ignore = "0.4.33" imagesize.workspace = true serde.workspace = true diff --git a/plugins/workspace/workspace-files/engine/README.md b/plugins/workspace/workspace-files/engine/README.md index 09eaab5fb17..63eaeb9f9a1 100644 --- a/plugins/workspace/workspace-files/engine/README.md +++ b/plugins/workspace/workspace-files/engine/README.md @@ -35,6 +35,6 @@ Text reads scan the original authorized file descriptor once per page, retaining `files` lists direct children through held directory capabilities and reports native invalidation hints. The caller supplies the original workspace identity, private Root/control paths and validated managed-worktree binding; caller paths cannot create that exception. `WorkspacePath` is shared with Git and preserves exact relative pathname bytes. Listing is live and bounded, with no snapshot cache. A watch registers the workspace-to-target directory chain before readiness, ignores read/access notifications, coalesces changes and revalidates names before delivery. The original worker must await `Watch::close` through normal native watch/callback cleanup, even after client cancellation. Native filesystem notifications are not a durable event history. -Grep and Glob return the complete matching set within their admitted scope. They buffer sources and results, check caller cancellation, and use iterative traversal with active-cycle detection; they impose no separate source-size, result-count, traversal-depth or whole-search deadline. Hidden, ignored, binary and symlink behavior remains part of each tool’s documented scope. Regex compilation retains the pinned library’s nesting and compiled-memory guards; this is not a promise to accept every mathematically valid regular expression. Current-file captures and readable write previews preserve complete original content. File listings use continuation pages with a soft byte target: one complete first entry can always advance. Git retains dependency representation limits and reduced-trust allocation policy; changing pathname length never grants repository authority. +Current-file captures and readable write previews preserve complete original content. File listings use continuation pages with a soft byte target: one complete first entry can always advance. Git retains dependency representation limits and reduced-trust allocation policy; changing pathname length never grants repository authority. [Source](src/lib.rs) · [Cargo manifest](Cargo.toml) diff --git a/plugins/workspace/workspace-files/engine/README.zh-CN.md b/plugins/workspace/workspace-files/engine/README.zh-CN.md index 934272a32d3..c74d8b3dc8f 100644 --- a/plugins/workspace/workspace-files/engine/README.zh-CN.md +++ b/plugins/workspace/workspace-files/engine/README.zh-CN.md @@ -35,6 +35,6 @@ `files` 通过持有的目录能力列出直接子项,并提供原生变更失效提示。调用方传入原工作区身份、私有 Root/control 路径及经过验证的受管 worktree 绑定;调用方路径不能自行创建这个例外。与 Git 共用的 `WorkspacePath` 保留准确的相对路径字节。列表采用有界实时分页,不保存快照缓存。监听在就绪前注册工作区到目标目录的祖先链,忽略读取/access 通知、合并变更,并在交付前重新校验目录名称。即使客户端取消,原 worker 也必须等待 `Watch::close` 完成正常的原生监听及回调清理。原生文件系统通知不是持久事件历史。 -Grep 和 Glob 返回授权搜索范围内的完整匹配集合,保留完整源数据和结果,并通过迭代遍历及活动循环检测检查调用方取消;不再设置独立的源大小、结果数量、遍历层数或整次搜索期限。隐藏、忽略、二进制及符号链接仍遵循各工具声明的范围。正则编译保留所固定依赖的嵌套与编译内存保护,不承诺接受所有数学上有效的正则表达式。当前文件捕获和可读取的写入前内容保留完整原文。文件列表使用可继续的分页及软字节目标,完整首项始终可以推进。Git 仍受依赖表示范围及降低信任时的分配策略约束;路径长度不会授予仓库权限。 +当前文件捕获和可读取的写入前内容保留完整原文。文件列表使用可继续的分页及软字节目标,完整首项始终可以推进。Git 仍受依赖表示范围及降低信任时的分配策略约束;路径长度不会授予仓库权限。 [源码](src/lib.rs) · [Cargo 配置](Cargo.toml) diff --git a/plugins/workspace/workspace-files/engine/src/files/candidates.rs b/plugins/workspace/workspace-files/engine/src/files/candidates.rs index 37ca97a32ab..5b5c93d59d9 100644 --- a/plugins/workspace/workspace-files/engine/src/files/candidates.rs +++ b/plugins/workspace/workspace-files/engine/src/files/candidates.rs @@ -167,7 +167,7 @@ fn disappeared(error: &io::Error) -> bool { } fn excluded(rules: &[[Gitignore; 4]], path: &Path, directory: bool) -> bool { - // Keep the same rule priority as Grep while allowing a nested negation to win. + // Let a nested negation override its parent rule. for priority in 0..4 { for layer in rules.iter().rev() { let matched = layer[priority].matched_path_or_any_parents(path, directory); diff --git a/plugins/workspace/workspace-files/engine/src/grep.rs b/plugins/workspace/workspace-files/engine/src/grep.rs deleted file mode 100644 index aa41b66083e..00000000000 --- a/plugins/workspace/workspace-files/engine/src/grep.rs +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -use crate::scoped::Directory as Dir; -use crate::{ReadExecutor, failed}; -use cap_std::fs::{File, OpenOptions, OpenOptionsExt}; -use maka_agent_facts::tools::ToolError; -use serde::Deserialize; -use serde_json::{Value, json}; -use std::{ - io::Read, - path::{Path, PathBuf}, -}; -use tokio_util::sync::CancellationToken; - -mod filter; -mod matches; - -pub const GREP_NAME: &str = "Grep"; -pub const GREP_DESCRIPTION: &str = "Search file contents with a ripgrep-compatible regular expression. Optional path selects a file or directory; glob filters files. Returns matches and complete. Results cover all matching lines in the admitted search scope. Hidden, binary and ignored files are excluded during directory traversal. No fuzzy fallback."; - -pub fn grep_schema() -> Value { - schemars::schema_for!(Input).into() -} - -#[derive(Deserialize, schemars::JsonSchema)] -#[serde(deny_unknown_fields)] -struct Input { - pattern: String, - #[serde(default = "default_path")] - path: String, - #[serde( - default, - deserialize_with = "present", - skip_serializing_if = "Option::is_none" - )] - #[schemars(with = "String")] - glob: Option, -} -fn default_path() -> String { - ".".into() -} -fn present<'de, D: serde::Deserializer<'de>>(d: D) -> Result, D::Error> { - String::deserialize(d).map(Some) -} - -impl ReadExecutor { - pub async fn grep( - &self, - input: Value, - cancellation: CancellationToken, - ) -> Result { - let input: Input = serde_json::from_value(input).map_err(|e| failed(e.to_string()))?; - if input.path.contains("://") { - return Err(failed("Grep requires a filesystem path")); - } - let matcher = matches::Regex::new(&input.pattern)?; - let authority = self.authority.clone(); - tokio::task::spawn_blocking(move || { - let mut search = Search { - matcher, - cancellation, - output: Vec::new(), - }; - search.check()?; - let requested = if input.path.is_empty() { - "." - } else { - &input.path - }; - let mut selected = None; - let mut error = failed("Grep path is outside the admitted Read roots"); - for route in authority.read_routes(Path::new(requested))? { - let relative = if route.relative.as_os_str().is_empty() { - PathBuf::from(".") - } else { - route.relative - }; - match open(&route.root, &relative) { - Ok(file) => { - selected = Some((route.root, route.root_path, relative, file)); - break; - } - Err(reason) => error = io_error(reason), - } - } - let (root, root_path, relative, file) = selected.ok_or(error)?; - let metadata = file.metadata().map_err(io_error)?; - if metadata.is_file() { - search.file(file, None)?; - } else if metadata.is_dir() { - let target = root.canonicalize(&relative).map_err(io_error)?; - let display: PathBuf = root_path.join(&target).components().collect(); - let display = dunce::simplified(&display); - let mut filters = filter::Filters::new( - input.glob.as_deref(), - dunce::simplified(authority.cwd()), - search.cancellation.clone(), - )?; - // Read ancestor rules only through the already admitted root. - for ancestor in target - .ancestors() - .skip(1) - .collect::>() - .into_iter() - .rev() - { - filters.push(&root, ancestor)?; - } - search.walk(&root, &target, &target, display, &mut filters)?; - } else { - return Err(failed("Grep supports only regular files and directories")); - } - search.check()?; - Ok(json!({"matches":search.output,"complete":true})) - }) - .await - .map_err(|e| failed(format!("Grep worker failed: {e}")))? - } -} - -struct Search { - matcher: matches::Regex, - cancellation: CancellationToken, - output: Vec, -} -impl Search { - fn check(&self) -> Result<(), ToolError> { - if self.cancellation.is_cancelled() { - return Err(failed("Grep cancelled")); - } - Ok(()) - } - fn file(&mut self, file: File, label: Option<&Path>) -> Result<(), ToolError> { - self.check()?; - let metadata = file.metadata().map_err(io_error)?; - if !metadata.is_file() { - return Err(failed("Grep target changed to a nonregular file")); - } - let mut bytes = Vec::new(); - let mut file = file.take(metadata.len()); - let mut chunk = [0; 8192]; - while file.limit() != 0 { - self.check()?; - let count = file.read(&mut chunk).map_err(io_error)?; - if count == 0 { - return Err(failed("Grep source changed within its captured extent")); - } - bytes.extend_from_slice(&chunk[..count]); - } - let decoded = encoding_rs::Encoding::for_bom(&bytes) - .map(|(encoding, _)| encoding.decode_with_bom_removal(&bytes).0); - let bytes = decoded - .as_ref() - .map_or(bytes.as_slice(), |text| text.as_bytes()); - if bytes.contains(&0) { - return Ok(()); - } - self.matcher - .search(bytes, label, &self.cancellation, &mut self.output)?; - self.check() - } - fn walk( - &mut self, - root: &Dir, - path: &Path, - target: &Path, - display: &Path, - filters: &mut filter::Filters, - ) -> Result<(), ToolError> { - enum Work { - Enter(PathBuf), - Child(PathBuf, bool, bool), - Leave(String), - } - let mut pending = vec![Work::Enter(path.to_owned())]; - let mut active = std::collections::BTreeSet::new(); - while let Some(work) = pending.pop() { - self.check()?; - match work { - Work::Leave(identity) => { - filters.pop(); - active.remove(&identity); - } - Work::Enter(path) => { - let identity = root - .open_dir(&path) - .and_then(|directory| directory.identity()) - .map_err(io_error)?; - if !active.insert(identity.clone()) { - continue; - } - filters.push(root, &path)?; - pending.push(Work::Leave(identity)); - let mut children = Vec::new(); - for entry in root.read_dir(&path).map_err(io_error)? { - self.check()?; - let entry = entry.map_err(io_error)?; - let kind = entry.file_type().map_err(io_error)?; - if !kind.is_dir() && !kind.is_file() { - continue; - } - #[cfg(unix)] - let hidden = false; - #[cfg(windows)] - let hidden = { - use cap_std::fs::MetadataExt; - entry.metadata().map_err(io_error)?.file_attributes() - & windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN - != 0 - }; - children.push((entry.file_name(), kind.is_dir(), hidden)); - } - children.sort_by(|a, b| a.0.cmp(&b.0)); - for (name, directory, hidden) in children.into_iter().rev() { - pending.push(Work::Child(path.join(name), directory, hidden)); - } - } - Work::Child(child, directory, hidden) => { - if let Err(error) = root.check_read(&child) { - if error.kind() == std::io::ErrorKind::PermissionDenied { - continue; - } - return Err(io_error(error)); - } - let label = display.join( - child - .strip_prefix(target) - .map_err(|e| failed(e.to_string()))?, - ); - if filters.excludes(&child, &label, directory, hidden) { - continue; - } - if directory { - pending.push(Work::Enter(child)); - } else { - self.file(open(root, &child).map_err(io_error)?, Some(&label))?; - } - } - } - } - Ok(()) - } -} - -fn open(root: &Dir, path: &Path) -> std::io::Result { - let mut options = OpenOptions::new(); - options.read(true); - #[cfg(unix)] - options.custom_flags(libc::O_NONBLOCK); - #[cfg(windows)] - options.custom_flags(windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS); - root.open_with(path, &options) -} -fn io_error(e: std::io::Error) -> ToolError { - ToolError::Io { - kind: e.kind(), - message: format!("Grep filesystem error: {e}"), - } -} diff --git a/plugins/workspace/workspace-files/engine/src/grep/filter.rs b/plugins/workspace/workspace-files/engine/src/grep/filter.rs deleted file mode 100644 index 159d28b9d54..00000000000 --- a/plugins/workspace/workspace-files/engine/src/grep/filter.rs +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -use super::io_error; -use crate::failed; -use crate::scoped::Directory as Dir; -use ignore::{ - gitignore::{Gitignore, GitignoreBuilder}, - overrides::{Override, OverrideBuilder}, -}; -use maka_agent_facts::tools::ToolError; -use std::{ - io::{self, Read}, - path::{Path, PathBuf}, -}; - -struct Layer { - rules: [Gitignore; 4], - in_git: bool, -} -pub(super) struct Filters { - cancellation: tokio_util::sync::CancellationToken, - layers: Vec, - overrides: Override, -} -impl Filters { - pub(super) fn new( - glob: Option<&str>, - cwd: &Path, - cancellation: tokio_util::sync::CancellationToken, - ) -> Result { - let mut builder = OverrideBuilder::new(cwd); - if let Some(glob) = glob.filter(|g| !g.is_empty()) { - builder - .add(glob) - .map_err(|e| failed(format!("Grep glob: {e}")))?; - } - Ok(Self { - cancellation, - layers: Vec::new(), - overrides: builder.build().map_err(|e| failed(e.to_string()))?, - }) - } - pub(super) fn push(&mut self, root: &Dir, path: &Path) -> Result<(), ToolError> { - let in_git = self.layers.last().is_some_and(|layer| layer.in_git) - || root.metadata(path.join(".git")).is_ok(); - let rules = [ - self.rules(root, path, ".rgignore")?, - self.rules(root, path, ".ignore")?, - if in_git { - self.rules(root, path, ".gitignore")? - } else { - Gitignore::empty() - }, - if in_git { - self.rules(root, path, ".git/info/exclude")? - } else { - Gitignore::empty() - }, - ]; - self.layers.push(Layer { rules, in_git }); - Ok(()) - } - pub(super) fn pop(&mut self) { - self.layers.pop(); - } - pub(super) fn excludes( - &self, - path: &Path, - display: &Path, - directory: bool, - os_hidden: bool, - ) -> bool { - let override_match = self.overrides.matched(display, directory); - if !override_match.is_none() { - return override_match.is_ignore(); - } - for priority in 0..4 { - for layer in self.layers.iter().rev() { - let matched = layer.rules[priority].matched_path_or_any_parents(path, directory); - if !matched.is_none() { - return matched.is_ignore(); - } - } - } - os_hidden || hidden(path) - } - fn rules(&mut self, root: &Dir, path: &Path, name: &str) -> Result { - let source: PathBuf = path.join(name); - let mut file = match super::open(root, &source) { - Ok(file) => file, - Err(error) - if matches!( - error.kind(), - io::ErrorKind::NotFound - | io::ErrorKind::NotADirectory - | io::ErrorKind::PermissionDenied - ) => - { - return Ok(Gitignore::empty()); - } - Err(error) => return Err(io_error(error)), - }; - let metadata = file.metadata().map_err(io_error)?; - if !metadata.is_file() { - return Err(failed("Grep ignore source must be a regular file")); - } - let mut bytes = Vec::new(); - let mut buffer = [0; 8192]; - let mut reader = (&mut file).take(metadata.len()); - while reader.limit() != 0 { - if self.cancellation.is_cancelled() { - return Err(failed("Grep cancelled")); - } - let count = reader.read(&mut buffer).map_err(io_error)?; - if count == 0 { - return Err(failed( - "Grep ignore source changed within its captured extent", - )); - } - bytes.extend_from_slice(&buffer[..count]); - } - let mut builder = GitignoreBuilder::new(path); - for line in String::from_utf8_lossy(&bytes).lines() { - builder - .add_line(Some(source.clone()), line) - .map_err(|e| failed(format!("Grep ignore rule: {e}")))?; - } - builder - .build() - .map_err(|e| failed(format!("Grep ignore rules: {e}"))) - } -} -fn hidden(path: &Path) -> bool { - path.file_name() - .is_some_and(|name| name.as_encoded_bytes().starts_with(b".")) -} diff --git a/plugins/workspace/workspace-files/engine/src/grep/matches.rs b/plugins/workspace/workspace-files/engine/src/grep/matches.rs deleted file mode 100644 index 39fc330a669..00000000000 --- a/plugins/workspace/workspace-files/engine/src/grep/matches.rs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -use crate::failed; -use grep_searcher::{Searcher, SearcherBuilder, Sink, SinkMatch}; -use maka_agent_facts::tools::ToolError; -use std::{io, path::Path}; -use tokio_util::sync::CancellationToken; - -pub(super) struct Regex(grep_regex::RegexMatcher); -impl Regex { - pub(super) fn new(pattern: &str) -> Result { - grep_regex::RegexMatcherBuilder::new() - .multi_line(true) - .line_terminator(Some(b'\n')) - .build(pattern) - .map(Self) - .map_err(|e| failed(format!("Grep regex: {e}"))) - } - pub(super) fn search( - &self, - bytes: &[u8], - label: Option<&Path>, - cancellation: &CancellationToken, - output: &mut Vec, - ) -> Result<(), ToolError> { - let mut sink = Lines { - cancellation, - label, - output, - }; - SearcherBuilder::new() - .line_number(true) - .bom_sniffing(false) // Decoded and bounded before binary detection. - .build() - .search_slice(&self.0, bytes, &mut sink) - .map_err(|e| failed(e.to_string()))?; - Ok(()) - } -} -struct Lines<'a> { - cancellation: &'a CancellationToken, - label: Option<&'a Path>, - output: &'a mut Vec, -} -impl Sink for Lines<'_> { - type Error = io::Error; - fn matched(&mut self, _: &Searcher, matched: &SinkMatch<'_>) -> Result { - if self.cancellation.is_cancelled() { - return Err(io::Error::other("Grep cancelled")); - } - let line = matched - .bytes() - .strip_suffix(b"\n") - .unwrap_or(matched.bytes()); - let line = String::from_utf8_lossy(line); - let number = matched - .line_number() - .ok_or_else(|| io::Error::other("Grep line number missing"))?; - let rendered = match self.label { - Some(label) => format!( - "{}:{number}:{line}", - label - .to_str() - .ok_or_else(|| io::Error::other("Grep path is not UTF-8"))? - ), - None => format!("{number}:{line}"), - }; - self.output.push(rendered); - Ok(true) - } -} diff --git a/plugins/workspace/workspace-files/engine/src/lib.rs b/plugins/workspace/workspace-files/engine/src/lib.rs index 0319423acbd..4595f14a21b 100644 --- a/plugins/workspace/workspace-files/engine/src/lib.rs +++ b/plugins/workspace/workspace-files/engine/src/lib.rs @@ -24,17 +24,13 @@ pub mod directory; pub mod files; pub mod git; pub mod image; -pub mod path; -mod search; -pub mod workspace; -pub mod worktree; -pub use search::{GLOB_DESCRIPTION, GLOB_NAME, glob_schema}; -mod grep; -pub use grep::{GREP_DESCRIPTION, GREP_NAME, grep_schema}; mod mutation; mod patch; +pub mod path; pub mod publication; mod scoped; +pub mod workspace; +pub mod worktree; mod write; mod write_target; @@ -177,18 +173,12 @@ impl ReadExecutor { impl ToolExecutor for ReadExecutor { fn names(&self) -> Vec { - vec![READ_NAME.into(), GLOB_NAME.into(), GREP_NAME.into()] + vec![READ_NAME.into()] } fn invoke(&self, name: String, input: Value, cancellation: CancellationToken) -> ToolFuture { let executor = self.clone(); Box::pin(async move { - if name == GLOB_NAME { - return executor.glob(input, cancellation).await; - } - if name == GREP_NAME { - return executor.grep(input, cancellation).await; - } if name != READ_NAME { return Err(failed("unsupported tool")); } diff --git a/plugins/workspace/workspace-files/engine/src/scoped.rs b/plugins/workspace/workspace-files/engine/src/scoped.rs index 33a76038499..853edf2eeff 100644 --- a/plugins/workspace/workspace-files/engine/src/scoped.rs +++ b/plugins/workspace/workspace-files/engine/src/scoped.rs @@ -53,7 +53,6 @@ pub(crate) struct Authority { pub(crate) struct Route { pub root: Directory, pub relative: PathBuf, - pub root_path: PathBuf, } enum CapturedScope { @@ -87,9 +86,6 @@ impl Authority { scope, }) } - pub(crate) fn cwd(&self) -> &Path { - &self.cwd - } pub(crate) fn publication_route(&self, relative: &Path) -> Result, ToolError> { let root = match &self.scope { @@ -108,7 +104,6 @@ impl Authority { self.policy.clone(), ), relative: relative.to_owned(), - root_path: root.path.clone(), })) } @@ -188,7 +183,6 @@ impl Authority { self.policy.clone(), ), relative: relative.to_owned(), - root_path: root.path.clone(), }]); } } @@ -250,7 +244,6 @@ impl Authority { .strip_prefix(root) .map_err(|_| failed("invalid Write path"))? .to_owned(), - root_path: root.to_owned(), }]) } CapturedScope::Restricted(roots) => path::candidates(roots, &absolute) @@ -263,7 +256,6 @@ impl Authority { self.policy.clone(), ), relative: relative.to_owned(), - root_path: root.path.clone(), }) }) .collect(), diff --git a/plugins/workspace/workspace-files/engine/src/scoped/directory.rs b/plugins/workspace/workspace-files/engine/src/scoped/directory.rs index 11383b8aab9..1d5b027af59 100644 --- a/plugins/workspace/workspace-files/engine/src/scoped/directory.rs +++ b/plugins/workspace/workspace-files/engine/src/scoped/directory.rs @@ -18,7 +18,7 @@ */ use cap_fs_ext::{DirExt, FollowSymlinks, OpenOptionsFollowExt}; -use cap_std::fs::{Dir, File, Metadata, OpenOptions, ReadDir}; +use cap_std::fs::{Dir, File, OpenOptions}; use maka_sandbox::filesystem::{Access, Compiled}; use std::{ io, @@ -62,13 +62,6 @@ impl Directory { Ok(()) } - pub(crate) fn check_read(&self, relative: &Path) -> io::Result<()> { - if self.policy.is_none() { - return Ok(()); - } - self.resolve(relative).map(|_| ()) - } - fn resolve(&self, relative: &Path) -> io::Result { // Parent components must retain real symlink/.. semantics; the canonical // capability path below is the authority for that spelling. @@ -101,65 +94,6 @@ impl Directory { parent.open_with(name, options.clone().follow(FollowSymlinks::No)) } - pub(crate) fn identity(&self) -> io::Result { - crate::directory::identity(&self.inner).map(String::from) - } - - pub(crate) fn open_dir(&self, relative: impl AsRef) -> io::Result { - let relative = relative.as_ref(); - let resolved = if self.policy.is_some() { - self.resolve(relative)? - } else { - self.inner.canonicalize(nonempty(relative))? - }; - let inner = if self.policy.is_some() { - walk(&self.inner, &resolved)? - } else { - self.inner.open_dir(nonempty(relative))? - }; - Ok(Self::new( - inner, - self.path.join(resolved), - self.policy.clone(), - )) - } - - pub(crate) fn read_dir(&self, relative: impl AsRef) -> io::Result { - self.open_dir(relative)?.inner.entries() - } - - pub(crate) fn canonicalize(&self, relative: impl AsRef) -> io::Result { - if self.policy.is_some() { - self.resolve(relative.as_ref()) - } else { - self.inner.canonicalize(nonempty(relative.as_ref())) - } - } - - pub(crate) fn metadata(&self, relative: impl AsRef) -> io::Result { - let relative = relative.as_ref(); - if self.policy.is_none() { - return self.inner.metadata(relative); - } - let resolved = self.resolve(relative)?; - let (parent, name) = self.parent(&resolved)?; - let metadata = parent.symlink_metadata(name)?; - if metadata.is_symlink() { - return Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "filesystem path changed during authorization", - )); - } - Ok(metadata) - } - - pub(crate) fn symlink_metadata(&self, relative: impl AsRef) -> io::Result { - if self.policy.is_some() { - self.check_read(relative.as_ref())?; - } - self.inner.symlink_metadata(relative) - } - /// Writes already refuse every symlink and parent component. Retain the /// directory capability after authorizing the exact name, including missing /// targets; read-only metadata cannot be created or replaced through Write. diff --git a/plugins/workspace/workspace-files/engine/src/search.rs b/plugins/workspace/workspace-files/engine/src/search.rs deleted file mode 100644 index b7ecf7a1d33..00000000000 --- a/plugins/workspace/workspace-files/engine/src/search.rs +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -use crate::scoped::Directory as Dir; -use crate::{ReadExecutor, failed}; -use glob::MatchOptions; -use maka_agent_facts::tools::ToolError; -use serde::Deserialize; -use serde_json::{Value, json}; -use std::{ - collections::BTreeSet, - path::{Path, PathBuf}, -}; -use tokio_util::sync::CancellationToken; - -mod pattern; -use pattern::{Segment, compile}; - -pub const GLOB_NAME: &str = "Glob"; -pub const GLOB_DESCRIPTION: &str = "Find paths matching a Unix glob (*, ?, **, character classes), relative to the search directory. Returns the complete matching file set in the admitted search scope. Wildcard matching is case-insensitive on macOS and Windows, case-sensitive on Linux. Hidden names require an explicit dot; recursive wildcards do not follow directory symlinks. cwd is limited to the Session's admitted filesystem roots."; - -pub fn glob_schema() -> Value { - schemars::schema_for!(Input).into() -} - -#[derive(Deserialize, schemars::JsonSchema)] -#[serde(deny_unknown_fields)] -struct Input { - #[schemars(length(min = 1))] - pattern: String, - #[serde(default = "default_cwd")] - #[schemars(length(min = 1))] - cwd: String, -} -fn default_cwd() -> String { - ".".into() -} - -impl ReadExecutor { - pub async fn glob( - &self, - input: Value, - cancellation: CancellationToken, - ) -> Result { - let input: Input = serde_json::from_value(input).map_err(|e| failed(e.to_string()))?; - let segments = compile(&input.pattern)?; - if input.cwd.is_empty() || input.cwd.contains("://") { - return Err(failed("Glob cwd requires a filesystem directory")); - } - let authority = self.authority.clone(); - tokio::task::spawn_blocking(move || { - let mut search = Search { - cancellation, - files: BTreeSet::new(), - }; - search.check()?; - let mut opened = None; - let mut error = failed("Glob cwd is outside the admitted Read roots"); - for route in authority.read_routes(Path::new(&input.cwd))? { - let root = route.root; - let path = route.relative; - match root.open_dir(if path.as_os_str().is_empty() { - Path::new(".") - } else { - &path - }) { - Ok(dir) => { - opened = Some(dir); - break; - } - Err(reason) => error = failed(format!("Glob cwd: {reason}")), - } - } - let directory = opened.ok_or(error)?; - if !segments.is_empty() { - search.walk(&directory, &segments)?; - } - search.check()?; - Ok(json!({"files":search.files,"complete":true})) - }) - .await - .map_err(|e| failed(format!("Glob worker failed: {e}")))? - } -} - -struct Search { - cancellation: CancellationToken, - files: BTreeSet, -} - -impl Search { - fn check(&self) -> Result<(), ToolError> { - if self.cancellation.is_cancelled() { - return Err(failed("Glob cancelled")); - } - Ok(()) - } - - fn record(&mut self, relative: &Path) -> Result<(), ToolError> { - let path = if relative.as_os_str().is_empty() { - "." - } else { - relative - .to_str() - .ok_or_else(|| failed("Glob path is not UTF-8"))? - }; - self.files.insert(path.to_owned()); - Ok(()) - } - - fn walk(&mut self, root: &Dir, segments: &[Segment]) -> Result<(), ToolError> { - enum Work { - Visit(PathBuf, usize), - Record(PathBuf), - Leave((String, usize)), - } - let mut pending = vec![Work::Visit(PathBuf::new(), 0)]; - let mut active = BTreeSet::new(); - while let Some(work) = pending.pop() { - self.check()?; - let (relative, index) = match work { - Work::Record(path) => { - self.record(&path)?; - continue; - } - Work::Leave(identity) => { - active.remove(&identity); - continue; - } - Work::Visit(path, index) => (path, index), - }; - if let Err(error) = root.check_read(&relative) { - if error.kind() == std::io::ErrorKind::PermissionDenied { - continue; - } - return Err(failed(format!("Glob path: {error}"))); - } - let Some(segment) = segments.get(index) else { - self.record(&relative)?; - continue; - }; - if matches!(segment, Segment::Directory) { - self.record(&relative)?; - continue; - } - let last = index + 1 == segments.len(); - if let Segment::Literal(name) = segment { - let child = relative.join(name); - let metadata = match root.symlink_metadata(&child) { - Ok(metadata) => metadata, - Err(error) if error.kind() == std::io::ErrorKind::NotFound => continue, - Err(error) => return Err(failed(format!("Glob path: {error}"))), - }; - if last { - self.record(&child)?; - } else if metadata.is_dir() || metadata.is_symlink() { - pending.push(Work::Visit(child, index + 1)); - } - continue; - } - let path = if relative.as_os_str().is_empty() { - Path::new(".") - } else { - &relative - }; - let identity = ( - root.open_dir(path) - .and_then(|directory| directory.identity()) - .map_err(|e| failed(format!("Glob path: {e}")))?, - index, - ); - if !active.insert(identity.clone()) { - continue; - } - pending.push(Work::Leave(identity)); - let recursive = matches!(segment, Segment::Recursive); - let mut children = Vec::new(); - for entry in root - .read_dir(path) - .map_err(|e| failed(format!("Glob directory: {e}")))? - { - self.check()?; - let entry = entry.map_err(|e| failed(format!("Glob directory entry: {e}")))?; - children.push(( - entry.file_name(), - entry - .file_type() - .map_err(|e| failed(format!("Glob file type: {e}")))?, - )); - } - children.sort_by(|a, b| a.0.cmp(&b.0)); - for (name, kind) in children.into_iter().rev() { - self.check()?; - let name = name - .to_str() - .ok_or_else(|| failed("Glob path is not UTF-8"))?; - let matched = match segment { - Segment::Recursive => !name.starts_with('.'), - Segment::Match(pattern) => pattern.matches_with( - name, - MatchOptions { - case_sensitive: cfg!(target_os = "linux"), - require_literal_separator: true, - require_literal_leading_dot: true, - }, - ), - _ => unreachable!("literal and directory handled above"), - }; - if !matched { - continue; - } - let child = relative.join(name); - if let Err(error) = root.check_read(&child) { - if error.kind() == std::io::ErrorKind::PermissionDenied { - continue; - } - return Err(failed(format!("Glob path: {error}"))); - } - if kind.is_dir() && (recursive || !last) { - pending.push(Work::Visit( - child.clone(), - if recursive { index } else { index + 1 }, - )); - } - if last { - pending.push(Work::Record(child)); - } - } - if recursive { - pending.push(Work::Visit(relative, index + 1)); - } - } - Ok(()) - } -} diff --git a/plugins/workspace/workspace-files/engine/src/search/pattern.rs b/plugins/workspace/workspace-files/engine/src/search/pattern.rs deleted file mode 100644 index 0e95425ff25..00000000000 --- a/plugins/workspace/workspace-files/engine/src/search/pattern.rs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -use crate::failed; -use glob::Pattern; -use maka_agent_facts::tools::ToolError; -use std::path::Path; - -pub(super) enum Segment { - Recursive, - Literal(String), - Match(Pattern), - Directory, -} - -pub(super) fn compile(pattern: &str) -> Result, ToolError> { - if pattern.is_empty() - || pattern.contains('\0') - || Path::new(pattern).is_absolute() - || pattern.split('/').any(|part| part == "..") - { - return Err(failed( - "Glob requires a relative pattern without parent traversal; select the search directory with cwd", - )); - } - if pattern.contains(['{', '}', '\\']) - || ["?(", "*(", "+(", "@(", "!("] - .iter() - .any(|part| pattern.contains(part)) - { - return Err(failed( - "Glob currently supports Unix wildcards, not brace expansion, escapes or extglobs", - )); - } - let mut segments = Vec::new(); - for part in pattern - .split('/') - .filter(|part| !part.is_empty() && *part != ".") - { - if part == "**" { - if !matches!(segments.last(), Some(Segment::Recursive)) { - segments.push(Segment::Recursive); - } - } else if !part.contains(['*', '?', '[']) { - segments.push(Segment::Literal(part.to_owned())); - } else { - segments.push(Segment::Match( - Pattern::new(part).map_err(|e| failed(format!("Glob pattern: {e}")))?, - )); - } - } - if !segments.is_empty() && pattern.ends_with('/') { - segments.push(Segment::Directory); - } - Ok(segments) -} diff --git a/plugins/workspace/workspace-files/engine/tests/behavior.rs b/plugins/workspace/workspace-files/engine/tests/behavior.rs index b02955469ed..eb5d97e84b1 100644 --- a/plugins/workspace/workspace-files/engine/tests/behavior.rs +++ b/plugins/workspace/workspace-files/engine/tests/behavior.rs @@ -18,9 +18,6 @@ */ mod edit; -mod glob; -mod grep; -mod grep_scope; mod patch; mod policy; mod read; diff --git a/plugins/workspace/workspace-files/engine/tests/glob.rs b/plugins/workspace/workspace-files/engine/tests/glob.rs deleted file mode 100644 index de3ef3bc03d..00000000000 --- a/plugins/workspace/workspace-files/engine/tests/glob.rs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -use maka_agent_facts::tools::ToolExecutor; -use maka_fs_tools::{ReadExecutor, ReadScope}; -use serde_json::json; -#[cfg(unix)] -use std::os::unix::fs::symlink; -use std::{fs, path::Path}; -use tokio_util::sync::CancellationToken; - -fn reader(root: &Path) -> ReadExecutor { - ReadExecutor::new( - root, - ReadScope::Restricted { - roots: vec![root.to_owned()], - }, - ) - .unwrap() -} - -#[tokio::test] -#[cfg(unix)] -async fn glob_keeps_captured_authority_and_cannot_walk_external_aliases_or_hide_failures() { - let temp = tempfile::tempdir().unwrap(); - let base = temp.path().canonicalize().unwrap(); - let root = base.join("root"); - fs::create_dir(&root).unwrap(); - fs::create_dir(base.join("outside")).unwrap(); - fs::write(base.join("outside/secret.txt"), "").unwrap(); - fs::write(root.join("inside.txt"), "").unwrap(); - symlink(base.join("outside"), root.join("escape")).unwrap(); - let executor = reader(&root); - fs::rename(&root, base.join("captured")).unwrap(); - fs::create_dir(&root).unwrap(); - fs::write(root.join("replacement.txt"), "").unwrap(); - assert_eq!( - executor - .invoke( - "Glob".into(), - json!({"pattern":"**/*.txt"}), - CancellationToken::new() - ) - .await - .unwrap(), - json!({"files":["inside.txt"],"complete":true}) - ); - for input in [ - json!({"pattern":"**","cwd":"escape"}), - json!({"pattern":"*","cwd":base.join("outside")}), - json!({"pattern":"../*"}), - json!({"pattern":"*","cwd":null}), - json!({"pattern":"*","cwd":"inside.txt"}), - json!({"pattern":"[bad"}), - json!({"pattern":"{a,b}"}), - json!({"pattern":"!(a)"}), - ] { - assert!( - executor - .invoke("Glob".into(), input, CancellationToken::new()) - .await - .is_err() - ); - } - let token = CancellationToken::new(); - token.cancel(); - assert!( - executor - .invoke("Glob".into(), json!({"pattern":"*"}), token) - .await - .is_err() - ); - let missing = executor - .invoke( - "Glob".into(), - json!({"pattern":"no-such-name"}), - CancellationToken::new(), - ) - .await - .unwrap(); - assert_eq!(missing, json!({"files":[],"complete":true})); -} diff --git a/plugins/workspace/workspace-files/engine/tests/grep.rs b/plugins/workspace/workspace-files/engine/tests/grep.rs deleted file mode 100644 index 53468754d31..00000000000 --- a/plugins/workspace/workspace-files/engine/tests/grep.rs +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -use maka_agent_facts::tools::ToolExecutor; -use maka_fs_tools::{ReadExecutor, ReadScope}; -use serde_json::{Value, json}; -#[cfg(unix)] -use std::os::unix::{ffi::OsStrExt, fs::symlink}; -use std::{fs, path::Path, process::Command}; -use tokio_util::sync::CancellationToken; - -fn reader(root: &Path) -> ReadExecutor { - ReadExecutor::new( - root, - ReadScope::Restricted { - roots: vec![root.to_owned()], - }, - ) - .unwrap() -} -async fn grep( - reader: &ReadExecutor, - input: Value, -) -> Result { - reader - .invoke("Grep".into(), input, CancellationToken::new()) - .await -} - -#[tokio::test] -async fn slice_search_matches_ripgrep_regex_lines_and_complete_ignore_scope() { - let temporary = tempfile::tempdir().unwrap(); - let root = temporary.path().canonicalize().unwrap(); - for directory in [".git/info", "src", "ignored", ".hidden"] { - fs::create_dir_all(root.join(directory)).unwrap(); - } - for (path, text) in [ - (".gitignore", "ignored/\n*.skip\nsrc/drop.txt\n"), - (".ignore", "!src/drop.txt\n!.hidden/\n"), - (".rgignore", "src/drop.txt\n"), - (".git/info/exclude", "excluded.txt\n"), - ("src/main.rs", "zero\nα token\r\nTOKEN --flag\ntoken\nlast"), - ("src/drop.txt", "token"), - ("src/other.txt", "token\ntoken\n"), - ("src/no.skip", "token"), - ("ignored/secret.txt", "token"), - (".hidden/file.txt", "token"), - ("excluded.txt", "token"), - ] { - fs::write(root.join(path), text).unwrap(); - } - fs::write(root.join("binary"), b"token\0hidden").unwrap(); - fs::write(root.join("many.txt"), "token\n".repeat(60)).unwrap(); - fs::write(root.join("utf8-bom.txt"), "\u{feff}token\n").unwrap(); - for (name, little_endian, bom) in [ - ("utf16-le.txt", true, [0xff, 0xfe]), - ("utf16-be.txt", false, [0xfe, 0xff]), - ] { - let mut bytes = bom.to_vec(); - for unit in "token\n漢字\n".encode_utf16() { - bytes.extend(if little_endian { - unit.to_le_bytes() - } else { - unit.to_be_bytes() - }); - } - fs::write(root.join(name), bytes).unwrap(); - } - #[cfg(windows)] - { - use std::os::windows::ffi::OsStrExt; - use windows_sys::Win32::Storage::FileSystem::{FILE_ATTRIBUTE_HIDDEN, SetFileAttributesW}; - fs::write(root.join("win-hidden.txt"), "token").unwrap(); - fs::create_dir(root.join("win-hidden-dir")).unwrap(); - fs::write(root.join("win-hidden-dir/file.txt"), "token").unwrap(); - for name in ["win-hidden.txt", "win-hidden-dir"] { - let path: Vec = root - .join(name) - .as_os_str() - .encode_wide() - .chain(Some(0)) - .collect(); - // SAFETY: the terminated path names only this test's file/directory. - assert_ne!( - unsafe { SetFileAttributesW(path.as_ptr(), FILE_ATTRIBUTE_HIDDEN) }, - 0 - ); - } - } - let executor = reader(&root); - for (pattern, path, glob) in [ - ("token", ".", None), - ("(?i)token", "src", None), - (r"\p{Greek}", ".", None), - ("--flag", ".", None), - ("^last$", "src/main.rs", None), - ("", "src/other.txt", None), - ("absent", ".", None), - ("token", ".", Some("*.txt")), - ("token", ".", Some("!*.txt")), - ("token", ".", Some("*.skip")), - ("token", "src/no.skip", None), - ("^token$", "utf8-bom.txt", None), - ("漢字", "utf16-le.txt", None), - ("漢字", "utf16-be.txt", None), - (r"\Atoken", "src/main.rs", None), - (r"(?-m)^token", "src/main.rs", None), - (r"token\z", "src/other.txt", None), - #[cfg(windows)] - ("token", "win-hidden.txt", None), - #[cfg(windows)] - ("token", ".", Some("*")), - ] { - // The original filesystem executor canonicalizes before invoking rg. - let target = root.join(path).canonicalize().unwrap(); - #[cfg(windows)] - let target = - std::path::PathBuf::from(target.to_str().unwrap().strip_prefix(r"\\?\").unwrap()); - let mut args = json!({"pattern":pattern, "path":target}); - let mut command = Command::new("rg"); - command.args(["--no-config", "-n", "--no-heading", "--color=never"]); - if let Some(glob) = glob { - args["glob"] = json!(glob); - command.args(["--glob", glob]); - } - let original = command - .arg("--") - .arg(pattern) - .arg(&target) - .current_dir(&root) - .output() - .unwrap(); - assert!( - matches!(original.status.code(), Some(0 | 1)), - "{}", - String::from_utf8_lossy(&original.stderr) - ); - let mut expected: Vec<_> = String::from_utf8_lossy(&original.stdout) - .split('\n') - .filter(|s| !s.is_empty()) - .map(String::from) - .collect(); - expected.sort(); - let result = grep(&executor, args).await.unwrap(); - let mut actual: Vec<_> = result["matches"] - .as_array() - .unwrap() - .iter() - .map(|v| v.as_str().unwrap().to_owned()) - .collect(); - actual.sort(); - assert_eq!(actual, expected, "{pattern:?} in {path} with {glob:?}"); - } - for n in 0..6 { - fs::write(root.join(format!("cap{n}.txt")), "CAP\n".repeat(60)).unwrap(); - } - fs::create_dir(root.join("z-later")).unwrap(); - fs::write(root.join("z-later/tail.txt"), "CAP\n").unwrap(); - let result = grep(&executor, json!({"pattern":"CAP"})).await.unwrap(); - let mut expected = (0..6) - .flat_map(|file| { - let path = root.join(format!("cap{file}.txt")); - (1..=60).map(move |line| format!("{}:{line}:CAP", path.display())) - }) - .collect::>(); - expected.push(format!("{}:1:CAP", root.join("z-later/tail.txt").display())); - let mut actual = result["matches"] - .as_array() - .unwrap() - .iter() - .map(|value| value.as_str().unwrap().to_owned()) - .collect::>(); - actual.sort(); - expected.sort(); - assert_eq!(actual, expected); - assert_eq!(result["complete"], true); -} - -#[tokio::test] -#[cfg(unix)] -async fn grep_preserves_capability_and_reports_regex_resource_and_cancellation_failures() { - let temporary = tempfile::tempdir().unwrap(); - let base = temporary.path().canonicalize().unwrap(); - let root = base.join("root"); - fs::create_dir(&root).unwrap(); - fs::write(base.join("outside.txt"), "OUTSIDE").unwrap(); - fs::write(root.join("inside.txt"), "INSIDE").unwrap(); - symlink(base.join("outside.txt"), root.join("escape.txt")).unwrap(); - let fifo = std::ffi::CString::new(root.join("fifo").as_os_str().as_bytes()).unwrap(); - assert_eq!(unsafe { libc::mkfifo(fifo.as_ptr(), 0o600) }, 0); - let executor = reader(&root); - fs::rename(&root, base.join("captured")).unwrap(); - fs::create_dir(&root).unwrap(); - fs::write(root.join("replacement.txt"), "OUTSIDE").unwrap(); - let result = grep(&executor, json!({"pattern":"INSIDE|OUTSIDE"})) - .await - .unwrap(); - assert_eq!( - result, - json!({"matches":[format!("{}:1:INSIDE",root.join("inside.txt").display())],"complete":true}) - ); - for args in [ - json!({"pattern":"[bad"}), - json!({"pattern":r"\n"}), - json!({"pattern":"x","path":"escape.txt"}), - json!({"pattern":"x","path":"fifo"}), - json!({"pattern":"x","path":base.join("outside.txt")}), - json!({"pattern":"x","glob":null}), - ] { - assert!(grep(&executor, args).await.is_err()); - } - fs::write( - base.join("captured/large"), - vec![b'x'; 10 * 1024 * 1024 + 1], - ) - .unwrap(); - assert_eq!( - grep(&executor, json!({"pattern":"absent","path":"large"})) - .await - .unwrap(), - json!({"matches":[],"complete":true}) - ); - let token = CancellationToken::new(); - token.cancel(); - assert!( - executor - .invoke("Grep".into(), json!({"pattern":"INSIDE"}), token) - .await - .is_err() - ); -} diff --git a/plugins/workspace/workspace-files/engine/tests/grep_scope.rs b/plugins/workspace/workspace-files/engine/tests/grep_scope.rs deleted file mode 100644 index e5c406a9082..00000000000 --- a/plugins/workspace/workspace-files/engine/tests/grep_scope.rs +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -use maka_agent_facts::tools::ToolExecutor; -use maka_fs_tools::{ReadExecutor, ReadScope}; -use serde_json::json; -use std::{fs, process::Command}; -use tokio_util::sync::CancellationToken; - -#[tokio::test] -async fn slash_glob_uses_session_cwd_independently_of_the_granted_root() { - let temporary = tempfile::tempdir().unwrap(); - let root = temporary.path().canonicalize().unwrap(); - let cwd = root.join("app"); - fs::create_dir_all(cwd.join("src")).unwrap(); - fs::write(cwd.join("src/main.rs"), "token\n").unwrap(); - let output = Command::new("rg") - .args([ - "--no-config", - "-n", - "--no-heading", - "--color=never", - "--glob", - "src/*.rs", - "--", - "token", - ]) - .arg(dunce::simplified(&cwd)) - .current_dir(&cwd) - .output() - .unwrap(); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); - let expected: Vec<_> = String::from_utf8(output.stdout) - .unwrap() - .lines() - .map(str::to_owned) - .collect(); - assert_eq!(expected.len(), 1); - for roots in [vec![root.clone()], vec![root.clone(), cwd.clone()]] { - let executor = ReadExecutor::new(&cwd, ReadScope::Restricted { roots }).unwrap(); - for path in [".", "src/../src"] { - let result = executor - .invoke( - "Grep".into(), - json!({"pattern":"token","glob":"src/*.rs","path":path}), - CancellationToken::new(), - ) - .await - .unwrap(); - assert_eq!( - result, - json!({"matches":expected,"complete":true}), - "path={path}" - ); - } - } -} diff --git a/plugins/workspace/workspace-files/engine/tests/policy.rs b/plugins/workspace/workspace-files/engine/tests/policy.rs index 822b4ae7e90..a923634e1a7 100644 --- a/plugins/workspace/workspace-files/engine/tests/policy.rs +++ b/plugins/workspace/workspace-files/engine/tests/policy.rs @@ -24,7 +24,7 @@ use serde_json::json; use std::{fs, sync::Arc}; #[tokio::test] -async fn read_search_and_mutations_share_nested_policy_including_missing_targets_and_aliases() { +async fn read_and_mutations_share_nested_policy_including_missing_targets_and_aliases() { let temp = tempfile::tempdir().unwrap(); let root = dunce::canonicalize(temp.path()).unwrap(); for dir in ["src", "metadata", "private"] { @@ -101,25 +101,6 @@ async fn read_search_and_mutations_share_nested_policy_including_missing_targets fs::read_to_string(root.join("private/token")).unwrap(), "needle secret" ); - fs::remove_file(root.join("src/linked")).unwrap(); - let matches = read - .invoke( - "Grep".into(), - json!({"pattern":"needle"}), - Default::default(), - ) - .await - .unwrap(); - assert_eq!(matches["complete"], true); - assert_eq!(matches["matches"].as_array().unwrap().len(), 2); - assert!(!matches.to_string().contains("secret")); - let paths = read - .invoke("Glob".into(), json!({"pattern":"**/*"}), Default::default()) - .await - .unwrap(); - assert_eq!(paths["complete"], true); - assert!(!paths.to_string().contains("private")); - assert!(!paths.to_string().contains("secret")); #[cfg(unix)] { std::os::unix::fs::symlink("../private", root.join("src/alias")).unwrap(); @@ -133,15 +114,6 @@ async fn read_search_and_mutations_share_nested_policy_including_missing_targets .await .is_err() ); - assert!( - read.invoke( - "Grep".into(), - json!({"path":"src/alias", "pattern":"needle"}), - Default::default() - ) - .await - .is_err() - ); assert!( read.invoke( "Read".into(), diff --git a/plugins/workspace/workspace-files/native/src/lib.rs b/plugins/workspace/workspace-files/native/src/lib.rs index cc4a80b546e..f0194ab26a7 100644 --- a/plugins/workspace/workspace-files/native/src/lib.rs +++ b/plugins/workspace/workspace-files/native/src/lib.rs @@ -501,35 +501,26 @@ async fn execute( } else { ReadExecutor::new(captured.path, ReadScope::Unrestricted)? }; - if tool == maka_fs_tools::READ_NAME { - let input: maka_agent_facts::read::ReadInput = - serde_json::from_value(input).map_err(failed)?; - match executor - .read( - input.resolve().map_err(failed)?, - read_page_budget(), - cancellation.clone(), - ) - .await? - { - ReadOutput::Text(_) if image => { - Err(failed("Workspace readImage requires image content")) - } - ReadOutput::Text(page) => { - serde_json::to_value(page).map(Output::Json).map_err(failed) - } - ReadOutput::Image { bytes, mime_type } if image => { - Ok(Output::Image { bytes, mime_type }) - } - ReadOutput::Image { .. } => { - Err(failed("Workspace readText accepts UTF-8 text, not images")) - } + let input: maka_agent_facts::read::ReadInput = + serde_json::from_value(input).map_err(failed)?; + match executor + .read( + input.resolve().map_err(failed)?, + read_page_budget(), + cancellation.clone(), + ) + .await? + { + ReadOutput::Text(_) if image => { + Err(failed("Workspace readImage requires image content")) + } + ReadOutput::Text(page) => serde_json::to_value(page).map(Output::Json).map_err(failed), + ReadOutput::Image { bytes, mime_type } if image => { + Ok(Output::Image { bytes, mime_type }) + } + ReadOutput::Image { .. } => { + Err(failed("Workspace readText accepts UTF-8 text, not images")) } - } else { - executor - .invoke(tool.into(), input, cancellation.clone()) - .await - .map(Output::Json) } } } diff --git a/products/maka/bundle/src/composition.ts b/products/maka/bundle/src/composition.ts index 88397433ffd..e679b58f2d7 100644 --- a/products/maka/bundle/src/composition.ts +++ b/products/maka/bundle/src/composition.ts @@ -474,7 +474,7 @@ function agentComposition(configuration: CompositionConfiguration): ApplicationS const contract = tool.target.kind === 'import' && tool.target.name === 'files' && - ['readText', 'glob', 'grep', 'write', 'edit', 'applyPatch'].includes(tool.method) + ['readText', 'write', 'edit', 'applyPatch'].includes(tool.method) ? 'maka.workspace.Files' : tool.target.kind === 'import' && tool.target.name === 'processes' && diff --git a/products/maka/bundle/src/installation.ts b/products/maka/bundle/src/installation.ts index cda8df34503..b78f1000f5e 100644 --- a/products/maka/bundle/src/installation.ts +++ b/products/maka/bundle/src/installation.ts @@ -53,8 +53,6 @@ export function installation( }); add('files', 'readText', 'Read', 'Read text from a file in the selected workspace.'); add('files', 'readImage', 'ReadImage', 'Read an image in the selected workspace.', 'image'); - add('files', 'glob', 'Glob', 'Find files in the selected workspace.'); - add('files', 'grep', 'Grep', 'Search file contents in the selected workspace.'); add('files', 'write', 'Write', 'Write a file in the selected workspace.'); add('files', 'edit', 'Edit', 'Edit a file in the selected workspace.'); add('files', 'applyPatch', 'ApplyPatch', 'Apply a patch in the selected workspace.'); diff --git a/products/maka/cli/DEPENDENCIES.rust.tsv b/products/maka/cli/DEPENDENCIES.rust.tsv index f7b2c2cd414..cd48859d883 100644 --- a/products/maka/cli/DEPENDENCIES.rust.tsv +++ b/products/maka/cli/DEPENDENCIES.rust.tsv @@ -389,9 +389,6 @@ gobject-sys@0.22.9 X granit-parser@1.3.0 X X graphene-rs@0.22.8 X graphene-sys@0.22.9 X -grep-matcher@0.1.9 X X -grep-regex@0.1.14 X X -grep-searcher@0.1.17 X X group@0.14.0 X X gsk4-sys@0.11.5 X gsk4@0.11.5 X diff --git a/products/maka/cli/THIRD_PARTY_NOTICES.txt b/products/maka/cli/THIRD_PARTY_NOTICES.txt index aee1850d82a..8f12c179747 100644 --- a/products/maka/cli/THIRD_PARTY_NOTICES.txt +++ b/products/maka/cli/THIRD_PARTY_NOTICES.txt @@ -1878,9 +1878,6 @@ byteorder-lite@0.1.0 (Unlicense OR MIT) byteorder@1.5.0 (Unlicense OR MIT); authors declared by package: Andrew Gallant encoding_rs_io@0.1.8 (MIT OR Apache-2.0); authors declared by package: Andrew Gallant globset@0.4.20 (Unlicense OR MIT); authors declared by package: Andrew Gallant -grep-matcher@0.1.9 (Unlicense OR MIT); authors declared by package: Andrew Gallant -grep-regex@0.1.14 (Unlicense OR MIT); authors declared by package: Andrew Gallant -grep-searcher@0.1.17 (Unlicense OR MIT); authors declared by package: Andrew Gallant ignore@0.4.33 (Unlicense OR MIT); authors declared by package: Andrew Gallant jiff-core@0.1.1 (Unlicense OR MIT); authors declared by package: Andrew Gallant jiff-static@0.2.38 (Unlicense OR MIT); authors declared by package: Andrew Gallant @@ -1896,9 +1893,6 @@ https://crates.io/api/v1/crates/byteorder-lite/0.1.0/download :: LICENSE-MIT https://crates.io/api/v1/crates/byteorder/1.5.0/download :: LICENSE-MIT https://crates.io/api/v1/crates/encoding_rs_io/0.1.8/download :: LICENSE-MIT https://crates.io/api/v1/crates/globset/0.4.20/download :: LICENSE-MIT -https://crates.io/api/v1/crates/grep-matcher/0.1.9/download :: LICENSE-MIT -https://crates.io/api/v1/crates/grep-regex/0.1.14/download :: LICENSE-MIT -https://crates.io/api/v1/crates/grep-searcher/0.1.17/download :: LICENSE-MIT https://crates.io/api/v1/crates/ignore/0.4.33/download :: LICENSE-MIT https://crates.io/api/v1/crates/jiff-core/0.1.1/download :: LICENSE-MIT https://crates.io/api/v1/crates/jiff-static/0.2.38/download :: LICENSE-MIT @@ -1937,9 +1931,6 @@ aho-corasick@1.1.5 (Unlicense OR MIT); authors declared by package: Andrew Galla byteorder-lite@0.1.0 (Unlicense OR MIT) byteorder@1.5.0 (Unlicense OR MIT); authors declared by package: Andrew Gallant globset@0.4.20 (Unlicense OR MIT); authors declared by package: Andrew Gallant -grep-matcher@0.1.9 (Unlicense OR MIT); authors declared by package: Andrew Gallant -grep-regex@0.1.14 (Unlicense OR MIT); authors declared by package: Andrew Gallant -grep-searcher@0.1.17 (Unlicense OR MIT); authors declared by package: Andrew Gallant ignore@0.4.33 (Unlicense OR MIT); authors declared by package: Andrew Gallant jiff-core@0.1.1 (Unlicense OR MIT); authors declared by package: Andrew Gallant jiff-static@0.2.38 (Unlicense OR MIT); authors declared by package: Andrew Gallant @@ -1957,9 +1948,6 @@ https://crates.io/api/v1/crates/aho-corasick/1.1.5/download :: UNLICENSE https://crates.io/api/v1/crates/byteorder-lite/0.1.0/download :: UNLICENSE https://crates.io/api/v1/crates/byteorder/1.5.0/download :: UNLICENSE https://crates.io/api/v1/crates/globset/0.4.20/download :: UNLICENSE -https://crates.io/api/v1/crates/grep-matcher/0.1.9/download :: UNLICENSE -https://crates.io/api/v1/crates/grep-regex/0.1.14/download :: UNLICENSE -https://crates.io/api/v1/crates/grep-searcher/0.1.17/download :: UNLICENSE https://crates.io/api/v1/crates/ignore/0.4.33/download :: UNLICENSE https://crates.io/api/v1/crates/jiff-core/0.1.1/download :: UNLICENSE https://crates.io/api/v1/crates/jiff-static/0.2.38/download :: UNLICENSE diff --git a/products/maka/cli/notices.json b/products/maka/cli/notices.json index 87a1fc69dc2..16877ea271f 100644 --- a/products/maka/cli/notices.json +++ b/products/maka/cli/notices.json @@ -413,9 +413,6 @@ "cargo:granit-parser@1.3.0 registry+https://github.com/rust-lang/crates.io-index": "825f4f68c60d44726dd40ae07cddcb56c5c9a0e395bada4b2acf367a76066635", "cargo:graphene-rs@0.22.8 registry+https://github.com/rust-lang/crates.io-index": "4cfd5b2996eed68ba8d1415209b44f06777a611ba90c04621e9413da618fadf6", "cargo:graphene-sys@0.22.9 registry+https://github.com/rust-lang/crates.io-index": "d11543865c3304ac61a98bc6b4e0639242dcf6fcd999bbe5ecf76eed61111eac", - "cargo:grep-matcher@0.1.9 registry+https://github.com/rust-lang/crates.io-index": "7b8ba5926cc4cdf4016d1c4e0888540f5a79a652485f3b60080562528dafa96a", - "cargo:grep-regex@0.1.14 registry+https://github.com/rust-lang/crates.io-index": "5a7bf8f95223f61ad6851b404c34048b70f041e3a1cbc25a254699d6192bb989", - "cargo:grep-searcher@0.1.17 registry+https://github.com/rust-lang/crates.io-index": "fa9fa4353725d4ee52476f0c9b2a481c9531d1dd111c32858098b4f30cbe5715", "cargo:group@0.14.0 registry+https://github.com/rust-lang/crates.io-index": "c45abc118b49e86c5fd855fe04e44c6119d66719eafb8622d46c0645c087df03", "cargo:gsk4-sys@0.11.5 registry+https://github.com/rust-lang/crates.io-index": "5a6408d53197c1554193ca33283334824915525daab63f7a95c00196eeaefc2d", "cargo:gsk4@0.11.5 registry+https://github.com/rust-lang/crates.io-index": "b9d145be27e9c93cfeaaeb03098af73ca58f8c110427b1369b62b8e46e5c8f4d", @@ -1249,5 +1246,5 @@ "npm:ajv@8.20.0 https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==": "e0b7c0627c7c460c99e8d7b8ac586e4c975ea2a2fae3859fdc94e67ec3aa69cc", "npm:pele@1.13.0 https://registry.npmjs.org/pele/-/pele-1.13.0.tgz sha512-Ie8UKY9x00MsuR3jO6AzUwzK2Z6MIV3Tf4EDdITiuk8Uaz8K7g0R082WqsqNaSU8DdgkrRvZThzhit83mi/uiw==": "4fa75f9a04ed6fb14b9abd85c484443e73dfb84762d829f77406ee9a1b31189b" }, - "sha256": "17464dd4ca5fb5db4a79588d91c6ea7d5b9dd996080f520c50977b9b32c21b4a" + "sha256": "72349c9b6279dd891cb6f5d9f0b84497cbb229a3f4ff2624ce8130839120d2c5" }