@agentdevjs/shell-feature 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -1,23 +1,23 @@
1
- # @agentdevjs/shell-feature
2
-
3
- Shell execution feature for AgentDev framework.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install @agentdevjs/shell-feature
9
- ```
10
-
11
- ## Usage
12
-
13
- ```typescript
14
- import { BasicAgent } from 'agentdev';
15
- import { ShellFeature } from '@agentdevjs/shell-feature';
16
-
17
- const agent = new BasicAgent().use(new ShellFeature());
18
- ```
19
-
20
- ## Features
21
-
22
- - **Bash Execution**: Execute shell commands via Git Bash
23
- - **Safe Trash**: Delete, list, and restore files safely with trash bin
1
+ # @agentdevjs/shell-feature
2
+
3
+ Shell execution feature for AgentDev framework.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @agentdevjs/shell-feature
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { BasicAgent } from 'agentdev';
15
+ import { ShellFeature } from '@agentdevjs/shell-feature';
16
+
17
+ const agent = new BasicAgent().use(new ShellFeature());
18
+ ```
19
+
20
+ ## Features
21
+
22
+ - **Bash Execution**: Execute shell commands via Git Bash
23
+ - **Safe Trash**: Delete, list, and restore files safely with trash bin
package/dist/index.d.ts CHANGED
@@ -46,15 +46,17 @@ interface ShellRunContext {
46
46
  * 截断输出并持久化完整内容到磁盘。
47
47
  *
48
48
  * 当输出超过 limit 时:
49
- * 1. 将完整输出写入 workdir/.agentdev/temp/bash-output-<timestamp>-<random>.log
49
+ * 1. 将完整输出写入 workdir/.agentdev/temp/<filePrefix>-<timestamp>-<random>.log
50
+ * (filePrefix 缺省 bash-output;非 bash 调用方传自己的工具名,便于溯源)
50
51
  * 2. 返回截断版本(头 60% + 尾 40%),中间插入截断提示和文件路径引用
51
52
  *
52
53
  * 如果写盘失败,fallback 到纯截断(不丢失截断提示,但完整内容不可恢复)。
53
54
  *
54
55
  * @param forcePersist true 时无论长度一律落盘(终止态使用:被杀进程的已积累
55
56
  * 输出必须可恢复),并返回 logPath 供元数据引用。
57
+ * @param filePrefix 落盘文件名前缀(缺省 'bash-output',bash 工具行为不变)
56
58
  */
57
- declare function processOutputWithPersistence(output: string, workdir: string, limit?: number, forcePersist?: boolean): Promise<[string, string | null]>;
59
+ declare function processOutputWithPersistence(output: string, workdir: string, limit?: number, forcePersist?: boolean, filePrefix?: string): Promise<[string, string | null]>;
58
60
  interface ShellMetadataFields {
59
61
  terminated: boolean;
60
62
  reason: ToolTerminationReason;
package/dist/index.js CHANGED
@@ -84,12 +84,12 @@ function timestampSlug() {
84
84
  const suffix = Math.random().toString(36).slice(2, 8);
85
85
  return `${ts}-${suffix}`;
86
86
  }
87
- async function processOutputWithPersistence(output, workdir, limit = MAX_OUTPUT_LENGTH, forcePersist = false) {
87
+ async function processOutputWithPersistence(output, workdir, limit = MAX_OUTPUT_LENGTH, forcePersist = false, filePrefix = "bash-output") {
88
88
  if (!forcePersist && output.length <= limit) return [output, null];
89
89
  let filePath = null;
90
90
  try {
91
91
  const tempDir = path.join(workdir, ".agentdev", "temp");
92
- const fileName = `bash-output-${timestampSlug()}.log`;
92
+ const fileName = `${filePrefix}-${timestampSlug()}.log`;
93
93
  filePath = path.join(tempDir, fileName);
94
94
  await mkdir(tempDir, { recursive: true });
95
95
  await writeFile(filePath, output, "utf-8");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/tools.ts","../src/shellQuoting.ts","../src/shell-core.ts","../src/powershell.ts","../src/tools-trash.ts","../src/lib/errors.ts","../src/lib/fs.ts","../src/lib/trashinfo.ts","../src/lib/trash.ts","../src/lib/restore.ts"],"sourcesContent":["/**\n * Shell Feature - 独立 npm 包\n *\n * 支持 Bash(Windows: Git Bash / Linux/macOS: 原生 bash)和 PowerShell 两种 Shell 环境。\n * 根据用户配置和运行时探测结果,条件注册 Bash 和/或 PowerShell 工具。\n *\n * @example\n * ```typescript\n * import { ShellFeature } from '@agentdevjs/shell-feature';\n * import { BasicAgent } from '@agentdevjs/core';\n *\n * const agent = new BasicAgent().use(new ShellFeature());\n * ```\n */\n\nimport { readFile } from 'fs/promises';\nimport { fileURLToPath } from 'url';\nimport { resolve } from 'path';\nimport type { AgentFeature, FeatureInitContext, FeatureManifestDefinition, PackageInfo } from '@agentdevjs/core';\nimport type { Tool } from '@agentdevjs/core';\nimport { getPackageInfoFromSource } from '@agentdevjs/core';\nimport { createShellCommandTool, findGitBashPath } from './tools.js';\nimport { createPowerShellTool, findPowerShellPath } from './powershell.js';\nimport { createSafeTrashDeleteTool, createSafeTrashListTool, createSafeTrashRestoreTool } from './tools-trash.js';\n\nconst __filename = fileURLToPath(import.meta.url);\n\nexport interface ShellFeatureConfig {\n workspaceDir?: string;\n workdir?: string;\n resourceRoot?: string;\n}\n\ninterface ResolvedShellConfig {\n bashEnabled: boolean;\n bashPath?: string;\n powershellEnabled: boolean;\n powershellPath?: string;\n /** 默认命令超时(毫秒),executor 计时起点;默认 120000 */\n defaultTimeoutMs: number;\n /** 超时硬上限(毫秒),任何来源的超时值 clamp 到此值;默认 600000 */\n maxTimeoutMs: number;\n}\n\n/** 默认超时契约值(与工具 timeout 声明保持一致,ticket 024 步骤 5)。 */\nconst DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes\nconst MAX_TIMEOUT_MS = 600_000; // 10 minutes\n\nfunction resolvePositiveNumber(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0\n ? Math.floor(value)\n : fallback;\n}\n\n/**\n * Shell Feature 实现\n */\nexport class ShellFeature implements AgentFeature {\n readonly name = 'shell';\n readonly dependencies: string[] = [];\n readonly source = __filename.replace(/\\\\/g, '/');\n readonly description = '提供 Bash/PowerShell 命令执行能力,以及安全删除、恢复和查看垃圾桶工具。';\n\n private bashDescription?: string;\n private powershellDescription?: string;\n private _packageInfo: PackageInfo | null = null;\n private readonly workspaceDir: string;\n private readonly workdir: string;\n private readonly resourceRoot: string;\n\n constructor(config: ShellFeatureConfig = {}) {\n this.workspaceDir = config.workspaceDir || process.cwd();\n this.workdir = config.workdir || this.workspaceDir;\n this.resourceRoot = config.resourceRoot || process.cwd();\n }\n\n /**\n * 获取同步工具(垃圾桶工具)\n */\n getTools(): Tool[] {\n return [\n createSafeTrashDeleteTool(this.workdir),\n createSafeTrashListTool(this.workdir),\n createSafeTrashRestoreTool(this.workdir),\n ];\n }\n\n getFeatureManifest(): FeatureManifestDefinition {\n return {\n schemaVersion: 1 as const,\n settings: {\n properties: {\n bashEnabled: {\n type: 'boolean',\n title: '启用 Bash',\n description: '启用后,Agent 将获得 Bash 工具。Windows 需要 Git for Windows;Linux/macOS 使用系统自带 Shell。',\n default: true,\n },\n bashPath: {\n type: 'file',\n title: 'Bash 路径',\n description: 'Bash 可执行文件路径。留空时自动检测。',\n placeholder: '自动检测',\n },\n powershellEnabled: {\n type: 'boolean',\n title: '启用 PowerShell',\n description: '启用后,Agent 将获得 PowerShell 工具。Windows 自带 PowerShell 5.1;Linux/macOS 需安装 PowerShell Core (pwsh)。',\n default: true,\n },\n powershellPath: {\n type: 'file',\n title: 'PowerShell 路径',\n description: 'PowerShell 可执行文件路径。留空时自动检测。',\n placeholder: '自动检测',\n },\n defaultTimeoutMs: {\n type: 'number',\n title: '默认命令超时(毫秒)',\n description: '命令执行的默认超时时间。模型可通过 timeout 参数覆盖(不超过最大超时)。',\n default: DEFAULT_TIMEOUT_MS,\n min: 1,\n max: MAX_TIMEOUT_MS,\n step: 1000,\n },\n maxTimeoutMs: {\n type: 'number',\n title: '最大命令超时(毫秒)',\n description: '命令超时的硬上限,任何来源的超时值都会被限制到此值以内。',\n default: MAX_TIMEOUT_MS,\n min: 1,\n step: 1000,\n },\n },\n },\n };\n }\n\n private resolveShellConfig(featureConfig: unknown): ResolvedShellConfig {\n if (!featureConfig || typeof featureConfig !== 'object') {\n return {\n bashEnabled: true,\n powershellEnabled: true,\n defaultTimeoutMs: DEFAULT_TIMEOUT_MS,\n maxTimeoutMs: MAX_TIMEOUT_MS,\n };\n }\n const c = featureConfig as Record<string, unknown>;\n const maxTimeoutMs = Math.max(\n resolvePositiveNumber(c.maxTimeoutMs, MAX_TIMEOUT_MS),\n 1,\n );\n return {\n bashEnabled: c.bashEnabled !== false,\n bashPath: typeof c.bashPath === 'string' && c.bashPath.trim() ? c.bashPath.trim() : undefined,\n powershellEnabled: c.powershellEnabled !== false,\n powershellPath: typeof c.powershellPath === 'string' && c.powershellPath.trim() ? c.powershellPath.trim() : undefined,\n // default 不允许超过 max(配置面板顺序无关时的自洽保护)\n defaultTimeoutMs: Math.min(\n resolvePositiveNumber(c.defaultTimeoutMs, DEFAULT_TIMEOUT_MS),\n maxTimeoutMs,\n ),\n maxTimeoutMs,\n };\n }\n\n /**\n * 获取异步工具(bash/powershell 工具,条件注册)\n */\n async getAsyncTools(ctx: FeatureInitContext): Promise<Tool[]> {\n const config = this.resolveShellConfig(ctx.featureConfig);\n const tools: Tool[] = [];\n\n // ── Bash 工具 ──\n if (config.bashEnabled) {\n const bashPath = findGitBashPath(config.bashPath);\n if (bashPath) {\n if (!this.bashDescription) {\n try {\n const descriptionPath = resolve(this.resourceRoot, '.agentdev/prompts/tool-bash.md');\n this.bashDescription = await readFile(descriptionPath, 'utf-8');\n } catch {\n this.bashDescription = '执行 Shell 命令';\n }\n }\n tools.push(createShellCommandTool(this.bashDescription, {\n workspaceDir: this.workspaceDir,\n workdir: this.workdir,\n resourceRoot: this.resourceRoot,\n bashPath,\n timeoutMs: config.defaultTimeoutMs,\n maxTimeoutMs: config.maxTimeoutMs,\n }));\n } else {\n console.warn('[shell] Bash is enabled but was not found on this system. Skipping Bash tool.');\n }\n }\n\n // ── PowerShell 工具 ──\n if (config.powershellEnabled) {\n const psPath = findPowerShellPath(config.powershellPath);\n if (psPath) {\n if (!this.powershellDescription) {\n try {\n const descriptionPath = resolve(this.resourceRoot, '.agentdev/prompts/tool-powershell.md');\n this.powershellDescription = await readFile(descriptionPath, 'utf-8');\n } catch {\n this.powershellDescription = '执行 PowerShell 命令';\n }\n }\n tools.push(createPowerShellTool(this.powershellDescription, {\n workspaceDir: this.workspaceDir,\n workdir: this.workdir,\n resourceRoot: this.resourceRoot,\n psPath,\n timeoutMs: config.defaultTimeoutMs,\n maxTimeoutMs: config.maxTimeoutMs,\n }));\n } else {\n console.warn('[shell] PowerShell is enabled but was not found on this system. Skipping PowerShell tool.');\n }\n }\n\n return tools;\n }\n\n /**\n * 获取包信息(统一打包方案)\n */\n getPackageInfo(): PackageInfo | null {\n if (!this._packageInfo) {\n this._packageInfo = getPackageInfoFromSource(this.source);\n }\n return this._packageInfo;\n }\n\n /**\n * 获取模板名称列表(统一打包方案)\n */\n getTemplateNames(): string[] {\n return [\n 'bash',\n 'trash-delete',\n 'trash-list',\n 'trash-restore',\n ];\n }\n}\n\n// 导出工具创建函数(供高级用户使用)\nexport { createShellCommandTool, runShellCommand, findGitBashPath } from './tools.js';\nexport type { ShellCommandToolOptions, ShellExecutionResult } from './tools.js';\nexport { createPowerShellTool, runPowerShellCommand, findPowerShellPath } from './powershell.js';\n\n// 导出共享运行核心(供高级用户使用;截断落盘函数自 tools.ts 迁入,行为不变)\nexport {\n processOutputWithPersistence,\n formatShellMetadata,\n SHELL_METADATA_OPEN,\n SHELL_METADATA_CLOSE,\n} from './shell-core.js';\nexport type {\n ShellRunResult,\n ShellMetadataFields,\n} from './shell-core.js';\n\n// 导出命令引用工具(供高级用户使用)\nexport {\n quoteShellCommand,\n shouldAddStdinRedirect,\n rewriteWindowsNullRedirect,\n containsHeredoc,\n hasStdinRedirect,\n} from './shellQuoting.js';\nexport {\n safeTrashDeleteTool,\n safeTrashListTool,\n safeTrashRestoreTool,\n createSafeTrashDeleteTool,\n createSafeTrashListTool,\n createSafeTrashRestoreTool,\n} from './tools-trash.js';\n\n// 导出库函数(供高级用户使用)\nexport { safeRm, listTrashed, restore } from './lib/index.js';\n","/**\n * Shell Feature 工具定义\n *\n * 提供 bash 工具,在 Windows 上通过 Git Bash、在 Linux/macOS 上通过原生 bash 执行 Shell 命令,支持 AbortSignal 中断。\n *\n * 改进点(照搬 Claude Code 的优秀实践):\n * 1. 命令引用:eval + 单引号包裹,彻底解决 syntax error near unexpected token '('\n * 2. 非 -i 模式:去掉 interactive flag,消除 job control 警告\n * 3. stdin redirect:自动添加 < /dev/null 防止命令挂起\n * 4. Windows null rewrite:>nul → >/dev/null\n * 5. 动态 bash 路径检测(Windows: Git Bash; Linux/macOS: $SHELL || /bin/bash)\n * 6. 输出截断:防止大输出撑爆 LLM 上下文\n *\n * 终止语义(ticket 024 / ADR-0005):超时计时归框架 executor(Tool.timeout\n * 声明契约);signal aborted 时 kill → drain 到 EOF → resolve 部分输出 +\n * <shell_metadata> 块(见 shell-core.ts)。\n */\n\nimport { execSync } from 'child_process';\nimport { existsSync } from 'fs';\nimport * as path from 'path';\nimport type { Tool } from '@agentdevjs/core';\nimport { createTool } from '@agentdevjs/core';\nimport {\n quoteShellCommand,\n shouldAddStdinRedirect,\n rewriteWindowsNullRedirect,\n} from './shellQuoting.js';\nimport {\n runCollectedProcess,\n // ShellRunContext 复用共享运行核心的上下文形状(signal/termination/progress)\n type ShellRunContext,\n} from './shell-core.js';\n\nexport interface ShellCommandToolOptions {\n workspaceDir?: string;\n workdir?: string;\n resourceRoot?: string;\n /** Override bash path detection (used when ShellFeature pre-detects the path) */\n bashPath?: string;\n /** 覆盖默认超时(manifest 配置 defaultTimeoutMs);缺省 120000 */\n timeoutMs?: number;\n /** 覆盖超时上限(manifest 配置 maxTimeoutMs);缺省 600000 */\n maxTimeoutMs?: number;\n}\n\nexport interface ShellExecutionResult {\n stdout: string;\n stderr: string;\n output: string;\n}\n\n// ---------------------------------------------------------------------------\n// 动态 Git Bash 路径检测(照搬 Claude Code 的 findGitBashPath)\n// ---------------------------------------------------------------------------\n\nlet cachedBashPath: string | null = null;\n\n/**\n * 动态查找 Git Bash 的 bash.exe 路径。\n *\n * 查找顺序:\n * 1. configuredPath 参数(来自 manifest 配置)\n * 2. 环境变量 AGENTDEV_GIT_BASH_PATH\n * 3. 环境变量 SHELL(如果包含 bash)\n * 4. where bash(Windows)\n * 5. 常见安装位置\n *\n * 返回 null 表示未找到(调用方应据此决定是否注册工具)。\n */\nexport function findGitBashPath(configuredPath?: string): string | null {\n if (cachedBashPath) return cachedBashPath;\n\n // 0. 用户在 manifest 中配置的路径\n if (configuredPath && existsSync(configuredPath)) {\n cachedBashPath = configuredPath;\n return cachedBashPath;\n }\n\n if (process.platform !== 'win32') {\n cachedBashPath = process.env.SHELL || '/bin/bash';\n return cachedBashPath;\n }\n\n const candidates: string[] = [];\n\n if (process.env.AGENTDEV_GIT_BASH_PATH) {\n candidates.push(process.env.AGENTDEV_GIT_BASH_PATH);\n }\n\n candidates.push('C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe');\n candidates.push('C:\\\\Program Files (x86)\\\\Git\\\\bin\\\\bash.exe');\n\n try {\n const result = execSync('where bash', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });\n for (const line of result.split('\\n').map(l => l.trim()).filter(Boolean)) {\n if (line.toLowerCase().includes('git')) {\n candidates.push(line);\n }\n }\n } catch {\n // where 命令可能不可用\n }\n\n try {\n const gitPath = execSync('where git', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).split('\\n')[0]?.trim();\n if (gitPath) {\n const derived = path.join(path.dirname(path.dirname(gitPath)), 'bin', 'bash.exe');\n candidates.push(derived);\n }\n } catch {\n // git 可能不在 PATH\n }\n\n for (const candidate of candidates) {\n if (candidate && existsSync(candidate)) {\n cachedBashPath = candidate;\n return cachedBashPath;\n }\n }\n\n cachedBashPath = null;\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// 核心运行参数与超时契约默认值\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes\nconst MAX_TIMEOUT_MS = 600_000; // 10 minutes\n\n/**\n * 运行 Shell 命令(支持 AbortSignal 中断;终止时收集部分输出并附元数据块)\n *\n * 关键改进:\n * - 使用 eval + 单引号引用替代 naive 的双引号转义\n * - 去掉 -i(interactive)flag\n * - 添加 stdin redirect\n * - 重写 Windows null redirect\n */\nexport async function runShellCommand(\n command: string,\n options: ShellCommandToolOptions = {},\n context?: ShellRunContext,\n): Promise<ShellExecutionResult> {\n const workspaceDir = options.workspaceDir || process.cwd();\n const workdir = options.workdir || workspaceDir;\n const resourceRoot = options.resourceRoot || process.cwd();\n const bashrcPath = resourceRoot.replace(/\\\\/g, '/') + '/.agentdev/bashrc';\n\n console.log(`[shell] ${command}`);\n\n // 1. 重写 Windows CMD 风格的 null redirect\n const normalizedCommand = rewriteWindowsNullRedirect(command);\n\n // 2. 安全引用命令\n const addStdinRedirect = shouldAddStdinRedirect(normalizedCommand);\n const quotedCommand = quoteShellCommand(normalizedCommand, addStdinRedirect);\n\n // 3. 构建 eval 命令字符串\n const quotedBashrc = `'${bashrcPath.replace(/'/g, `'\\\\''`)}'`;\n const commandString = `source ${quotedBashrc} 2>/dev/null || true; eval ${quotedCommand}`;\n\n // 4. 确定 bash 路径和参数\n const bashPath = options.bashPath || findGitBashPath();\n if (!bashPath) {\n const hint = process.platform === 'win32'\n ? 'Git Bash not found. Please install Git for Windows or configure the path in settings.'\n : 'Bash not found. Please ensure bash is installed or configure the path in settings.';\n throw new Error(hint);\n }\n\n const isWin = process.platform === 'win32';\n\n return runCollectedProcess({\n workdir,\n execPath: bashPath,\n args: ['-c', commandString],\n env: {\n ...process.env,\n // MSYSTEM is only meaningful for Git Bash (MSYS2/MinGW) on Windows.\n ...(isWin ? { MSYSTEM: process.env.MSYSTEM || 'MINGW64' } : {}),\n },\n logPrefix: '[shell]',\n signal: context?.signal,\n termination: context?.termination,\n terminationDeadline: context?.terminationDeadline,\n progress: context?.progress,\n cleanStderr: (stderr) => stderr\n .split('\\n')\n .filter(line => !line.includes('process group') && !line.includes('job control'))\n .join('\\n')\n .trim(),\n });\n}\n\nexport function createShellCommandTool(\n description: string,\n options: ShellCommandToolOptions = {},\n): Tool {\n const defaultTimeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n // max 是硬上限:default 配置超过 max 时收敛到 max,保证契约自洽\n const maxTimeoutMs = Math.max(options.maxTimeoutMs ?? MAX_TIMEOUT_MS, defaultTimeoutMs, 1);\n return createTool({\n name: 'bash',\n description,\n parameters: {\n type: 'object',\n properties: {\n command: { type: 'string' },\n timeout: { type: 'number', description: `Optional timeout in milliseconds (max ${maxTimeoutMs}). Defaults to ${defaultTimeoutMs}.` },\n },\n required: ['command'],\n },\n render: { call: 'bash', result: 'bash' },\n // 计时职责归框架 executor(ticket 023):args.timeout 经 fromArg 消费并 clamp\n timeout: {\n defaultMs: defaultTimeoutMs,\n maxMs: maxTimeoutMs,\n fromArg: 'timeout',\n },\n execute: async (args, context) => {\n const { command } = args as { command: string };\n // 生效超时(executor clamp 后)经 toolContext 透传(工单 025 进度显示)\n const effectiveTimeoutMs = typeof context?.timeoutMs === 'number' ? context.timeoutMs : null;\n const result = await runShellCommand(command, options, {\n signal: context?.signal,\n termination: context?.termination,\n terminationDeadline: context?.terminationDeadline,\n progress: {\n callId: context?.callId,\n toolName: 'bash',\n timeoutMs: effectiveTimeoutMs,\n },\n });\n return result.output;\n },\n });\n}\n","/**\n * Shell command quoting utilities.\n *\n * Ported from Claude Code's shellQuoting.ts + shellQuote.ts.\n *\n * 核心思路:用单引号整体包裹命令字符串,使所有特殊字符(括号、管道符、\n * 分号、$、反引号等)被 bash 视为字面量。然后通过 `eval` 进行二次解析。\n *\n * 这彻底解决了 `syntax error near unexpected token '('` 问题:\n * - 旧方案:command.replace(/\"/g, '\\\\\"') 只转义双引号,\n * 导致 `(`, `)`, `;`, `|` 等裸露在 bash 面前\n * - 新方案:单引号包裹整个命令,eval 二次解析\n */\n\n// ---------------------------------------------------------------------------\n// Heredoc detection\n// ---------------------------------------------------------------------------\n\n/**\n * 检测命令是否包含 heredoc 语法(<<EOF, <<'EOF', <<\"EOF\", <<-EOF 等)。\n * 排除位运算左移(<<)的误判。\n */\nexport function containsHeredoc(command: string): boolean {\n // 排除位运算:数字 << 数字、[[ 数字 << 数字 ]]、$(( ... << ... ))\n if (\n /\\d\\s*<<\\s*\\d/.test(command) ||\n /\\[\\[\\s*\\d+\\s*<<\\s*\\d+\\s*\\]\\]/.test(command) ||\n /\\$\\(\\(.*<<.*\\)\\)/.test(command)\n ) {\n return false;\n }\n\n const heredocRegex = /<<-?\\s*(?:(['\"]?)(\\w+)\\1|\\\\(\\w+))/;\n return heredocRegex.test(command);\n}\n\n// ---------------------------------------------------------------------------\n// Multiline string detection\n// ---------------------------------------------------------------------------\n\n/**\n * 检测命令中是否包含跨行的引号字符串。\n * 这些命令需要特殊引用处理。\n */\nfunction containsMultilineString(command: string): boolean {\n const singleQuoteMultiline = /'(?:[^'\\\\]|\\\\.)*\\n(?:[^'\\\\]|\\\\.)*'/;\n const doubleQuoteMultiline = /\"(?:[^\"\\\\]|\\\\.)*\\n(?:[^\"\\\\]|\\\\.)*\"/;\n return singleQuoteMultiline.test(command) || doubleQuoteMultiline.test(command);\n}\n\n// ---------------------------------------------------------------------------\n// Stdin redirect\n// ---------------------------------------------------------------------------\n\n/**\n * 检测命令是否已有 stdin 重定向(如 < file, </path, < /dev/null)。\n * 排除 << (heredoc)、<< (位运算)、<(进程替换)。\n */\nexport function hasStdinRedirect(command: string): boolean {\n return /(?:^|[\\s;&|])<(?![<(])\\s*\\S+/.test(command);\n}\n\n/**\n * 判断是否应该为命令添加 `< /dev/null` stdin 重定向。\n *\n * - heredoc 命令不需要(它们有自己的输入)\n * - 已有 stdin 重定向的命令不需要\n */\nexport function shouldAddStdinRedirect(command: string): boolean {\n if (containsHeredoc(command)) {\n return false;\n }\n if (hasStdinRedirect(command)) {\n return false;\n }\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Windows null redirect rewrite\n// ---------------------------------------------------------------------------\n\n/**\n * 将 Windows CMD 风格的 `>nul` 重写为 POSIX 的 `>/dev/null`。\n *\n * Git Bash 看到 `2>nul` 时会创建一个名为 `nul` 的字面量文件——这是\n * Windows 保留设备名,极难删除且会破坏 git 操作。\n *\n * 匹配:`>nul`, `> NUL`, `2>nul`, `&>nul`, `>>nul`(不区分大小写)\n * 不匹配:`>null`, `>nullable`, `>nul.txt`\n */\nconst NUL_REDIRECT_REGEX = /(\\d?&?>+\\s*)[Nn][Uu][Ll](?=\\s|$|[|&;)\\n])/g;\n\nexport function rewriteWindowsNullRedirect(command: string): string {\n return command.replace(NUL_REDIRECT_REGEX, '$1/dev/null');\n}\n\n// ---------------------------------------------------------------------------\n// Single-quote escaping\n// ---------------------------------------------------------------------------\n\n/**\n * 将字符串用 bash 单引号包裹,内部的 `'` 替换为 `'\"'\"'`。\n *\n * 这是 bash 中在单引号字符串内嵌入单引号的标准技巧:\n * - 结束当前单引号\n * - 用双引号包裹单引号\n * - 重新开始单引号\n *\n * @example\n * escapeForSingleQuote(\"it's\") → \"it'\\\"'\\\"'s\"\n * wrapSingleQuote(\"echo hello\") → \"'echo hello'\"\n * wrapSingleQuote(\"echo it's\") → \"'echo it'\\\"'\\\"'s'\"\n */\nfunction escapeForSingleQuote(str: string): string {\n return str.replace(/'/g, `'\\\"'\\\"'`);\n}\n\nfunction wrapSingleQuote(str: string): string {\n return `'${escapeForSingleQuote(str)}'`;\n}\n\n// ---------------------------------------------------------------------------\n// Main quoting function\n// ---------------------------------------------------------------------------\n\n/**\n * 对 shell 命令进行安全引用,保留 heredoc 和多行字符串的完整性。\n *\n * 返回的字符串可直接用于 `eval`,例如:\n * const quoted = quoteShellCommand('echo \"hello (world)\"');\n * // quoted === \"'echo \\\"hello (world)\\\"' < /dev/null\"\n * // 完整命令: eval 'echo \"hello (world)\"' < /dev/null\n *\n * @param command 要引用的原始命令\n * @param addStdinRedirect 是否追加 `< /dev/null` stdin 重定向\n * @returns 安全引用后的命令字符串\n */\nexport function quoteShellCommand(\n command: string,\n addStdinRedirect: boolean = true,\n): string {\n // heredoc 和多行字符串需要特殊处理\n // shell-quote 库会错误地转义这些内容中的 ! 为 \\!\n if (containsHeredoc(command) || containsMultilineString(command)) {\n const quoted = wrapSingleQuote(command);\n\n // heredoc 自带输入,不加 stdin redirect\n if (containsHeredoc(command)) {\n return quoted;\n }\n\n return addStdinRedirect ? `${quoted} < /dev/null` : quoted;\n }\n\n // 普通命令:单引号包裹\n const quoted = wrapSingleQuote(command);\n\n if (addStdinRedirect) {\n return `${quoted} < /dev/null`;\n }\n\n return quoted;\n}\n","/**\n * Shell 共享核心(ticket 024 / ADR-0005)\n *\n * bash 与 powershell 共用的「spawn + collect + 截断落盘 + 终止收集」管线:\n * - 正常完成:沿用既有语义(成功 stdout/stderr 独立截断落盘;失败合并截断后 reject)。\n * - 终止收集(toolContext.signal aborted,ticket 023 合并 signal 契约):\n * kill(Windows taskkill /PID <pid> /T /F 进程树;POSIX 进程组 SIGKILL)\n * → 继续读 stdout/stderr 到 EOF(上限 1s,孙进程占 pipe 兜底)\n * → resolve(不再 reject),结果文本为部分输出 + 尾部 <shell_metadata> 块。\n *\n * 计时职责归框架 executor(Tool.timeout 声明契约):本模块不设内部 setTimeout\n * race;args.timeout 由 executor 经 fromArg 消费并 clamp。\n */\n\nimport { spawn } from 'child_process';\nimport { mkdir, writeFile } from 'fs/promises';\nimport * as path from 'path';\nimport type { ToolTerminationReason } from '@agentdevjs/core';\nimport { emitNotification, createToolProgress } from '@agentdevjs/core';\n\n/** drain 上限:kill 后等待管道 EOF 的时间(孙进程占 pipe 兜底)。 */\nconst TERMINATION_DRAIN_FALLBACK_MS = 1000;\n/** Reserve a small margin so the tool promise settles before executor's deadline. */\nconst TERMINATION_DRAIN_SAFETY_MS = 50;\n\n/** 进度发射节流(工单 025):notification 层另有 100ms 节流兜底。 */\nconst PROGRESS_EMIT_INTERVAL_MS = 300;\n\n/** 进度 outputTail 行数上限。 */\nconst PROGRESS_TAIL_LINES = 5;\n\n/** 元数据块标记(仅终止态出现在结果尾部)。 */\nexport const SHELL_METADATA_OPEN = '<shell_metadata>';\nexport const SHELL_METADATA_CLOSE = '</shell_metadata>';\n\nexport interface ShellRunResult {\n stdout: string;\n stderr: string;\n output: string;\n}\n\nexport interface ShellRunContext {\n /** 框架注入的合并 signal(用户打断与框架超时共用,见 ticket 023) */\n signal?: AbortSignal;\n /** 终止原因查询(executor 注入;未走终止协议时恒返回 null) */\n termination?: () => ToolTerminationReason | null;\n /** 当前终止 settle 的绝对截止时间(epoch ms);shell drain 消费同一预算 */\n terminationDeadline?: () => number | null;\n /**\n * 进度发射上下文(工单 025):提供后执行中每 ~300ms 发射一次\n * tool.progress 通知;缺省不发射(纯增量,行为不变)。\n */\n progress?: {\n /** LLM 生成的 call.id */\n callId?: string;\n /** 工具名称 */\n toolName: string;\n /** 本次调用生效超时(毫秒);未声明 timeout 时为 null */\n timeoutMs: number | null;\n };\n}\n\n/** 取输出尾部 N 行(进度预览)。 */\nfunction tailLines(text: string, lines: number): string {\n if (!text) return '';\n const parts = text.split('\\n');\n const tail = parts.slice(-lines).join('\\n');\n return tail.length > 500 ? tail.slice(-500) : tail;\n}\n\n// ---------------------------------------------------------------------------\n// 输出截断 + 落盘持久化(自 tools.ts 迁入共享;bash-output-* 落盘机制不变)\n// ---------------------------------------------------------------------------\n\nconst MAX_OUTPUT_LENGTH = 30_000;\n\nfunction timestampSlug(): string {\n const now = new Date();\n const ts = now.getFullYear().toString() +\n String(now.getMonth() + 1).padStart(2, '0') +\n String(now.getDate()).padStart(2, '0') + '-' +\n String(now.getHours()).padStart(2, '0') +\n String(now.getMinutes()).padStart(2, '0') +\n String(now.getSeconds()).padStart(2, '0');\n const suffix = Math.random().toString(36).slice(2, 8);\n return `${ts}-${suffix}`;\n}\n\n/**\n * 截断输出并持久化完整内容到磁盘。\n *\n * 当输出超过 limit 时:\n * 1. 将完整输出写入 workdir/.agentdev/temp/bash-output-<timestamp>-<random>.log\n * 2. 返回截断版本(头 60% + 尾 40%),中间插入截断提示和文件路径引用\n *\n * 如果写盘失败,fallback 到纯截断(不丢失截断提示,但完整内容不可恢复)。\n *\n * @param forcePersist true 时无论长度一律落盘(终止态使用:被杀进程的已积累\n * 输出必须可恢复),并返回 logPath 供元数据引用。\n */\nexport async function processOutputWithPersistence(\n output: string,\n workdir: string,\n limit: number = MAX_OUTPUT_LENGTH,\n forcePersist: boolean = false,\n): Promise<[string, string | null]> {\n if (!forcePersist && output.length <= limit) return [output, null];\n\n // 尝试将完整输出持久化到磁盘\n let filePath: string | null = null;\n try {\n const tempDir = path.join(workdir, '.agentdev', 'temp');\n const fileName = `bash-output-${timestampSlug()}.log`;\n filePath = path.join(tempDir, fileName);\n\n await mkdir(tempDir, { recursive: true });\n await writeFile(filePath, output, 'utf-8');\n } catch (err) {\n console.error(`[shell] Failed to persist output: ${err}`);\n filePath = null;\n }\n\n if (output.length <= limit) return [output, filePath];\n\n const headSize = Math.floor(limit * 0.6);\n const tailSize = limit - headSize;\n const head = output.slice(0, headSize);\n const tail = output.slice(-tailSize);\n const omitted = output.length - limit;\n const totalKB = Math.round(output.length / 1024);\n\n const persistNotice = filePath\n ? `[Full output (${totalKB}KB) saved to: ${filePath}]\\nUse the read tool to access the full output if needed.\\n`\n : '';\n\n return [\n head +\n `\\n\\n... [truncated: omitted ${omitted} characters (${totalKB}KB total)] ...\\n${persistNotice}\\n` +\n tail,\n filePath,\n ];\n}\n\n// ---------------------------------------------------------------------------\n// kill 工具(Windows taskkill 进程树 / POSIX 进程组)\n// ---------------------------------------------------------------------------\n\nfunction makeKillChild(child: import('child_process').ChildProcess): () => void {\n return () => {\n try {\n if (process.platform === 'win32') {\n const killer = spawn('taskkill', ['/PID', String(child.pid), '/T', '/F'], { stdio: 'ignore' });\n killer.once('error', (error) => {\n console.warn(`[shell] taskkill failed for PID=${child.pid}:`, error);\n child.kill('SIGKILL');\n });\n killer.once('close', (code) => {\n if (code !== 0) {\n console.warn(`[shell] taskkill exited with code ${code} for PID=${child.pid}; killing the direct child`);\n child.kill('SIGKILL');\n }\n });\n } else {\n process.kill(-child.pid!, 'SIGKILL');\n }\n } catch {\n // 进程可能已经退出\n }\n };\n}\n\n/** kill 后等待管道 EOF:正常在 close 触发,孙进程继承 pipe 句柄时以超时兜底。 */\nfunction drainToEof(child: import('child_process').ChildProcess, timeoutMs: number): Promise<void> {\n return new Promise((resolve) => {\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(finish, timeoutMs);\n timer.unref();\n child.once('close', finish);\n });\n}\n\n// ---------------------------------------------------------------------------\n// <shell_metadata> 结果块(仅终止态出现,字段见工单 024 步骤 4)\n// ---------------------------------------------------------------------------\n\nexport interface ShellMetadataFields {\n terminated: boolean;\n reason: ToolTerminationReason;\n durationMs: number;\n exitCode: number | null;\n outputBytes: number;\n truncated: boolean;\n logPath: string | null;\n}\n\nexport function formatShellMetadata(meta: ShellMetadataFields): string {\n return [\n SHELL_METADATA_OPEN,\n `terminated: ${meta.terminated}`,\n `reason: ${meta.reason}`,\n `durationMs: ${Math.round(meta.durationMs)}`,\n `exitCode: ${meta.exitCode === null ? 'null' : meta.exitCode}`,\n `outputBytes: ${meta.outputBytes}`,\n `truncated: ${meta.truncated}`,\n `logPath: ${meta.logPath ?? 'null'}`,\n SHELL_METADATA_CLOSE,\n ].join('\\n');\n}\n\n// ---------------------------------------------------------------------------\n// 共享运行核心\n// ---------------------------------------------------------------------------\n\nexport interface SharedRunOptions extends ShellRunContext {\n workdir: string;\n execPath: string;\n args: string[];\n env?: NodeJS.ProcessEnv;\n /** 日志前缀('[shell]' / '[powershell]') */\n logPrefix: string;\n /** stderr 清理(bash 过滤 job control 噪音;powershell 仅 trim),在截断/合并前调用 */\n cleanStderr?: (stderr: string) => string;\n}\n\n/**\n * spawn + collect + 截断落盘 + 终止收集。\n *\n * - signal 未触发:行为与历史实现一致(成功 resolve 截断输出;失败 reject 截断信息)。\n * - signal aborted:kill → drain 到 EOF(≤1s)→ resolve 部分输出 + 尾部元数据块,\n * 已积累输出无条件落盘(logPath 入元数据)。\n */\nexport function runCollectedProcess(\n opts: SharedRunOptions,\n): Promise<ShellRunResult> {\n const { workdir, logPrefix, signal, termination, progress } = opts;\n\n const startedAt = Date.now();\n const cleanForRun = (raw: string): string =>\n opts.cleanStderr ? opts.cleanStderr(raw) : raw.trim();\n const readTermination = (): ToolTerminationReason => termination?.() ?? 'user';\n\n return new Promise<ShellRunResult>((resolve, reject) => {\n let stdout = '';\n let stderr = '';\n let settled = false;\n\n // 进度发射(工单 025):~300ms 节流,发射失败静默忽略(纯呈现通道)\n let lastProgressAt = 0;\n let progressTimer: ReturnType<typeof setInterval> | null = null;\n const stopProgressTimer = () => {\n if (progressTimer !== null) {\n clearInterval(progressTimer);\n progressTimer = null;\n }\n };\n const emitProgress = () => {\n if (!progress) return;\n const now = Date.now();\n if (now - lastProgressAt < PROGRESS_EMIT_INTERVAL_MS) return;\n lastProgressAt = now;\n try {\n emitNotification(createToolProgress({\n callId: progress.callId ?? '',\n toolName: progress.toolName,\n startedAt,\n elapsedMs: now - startedAt,\n timeoutMs: progress.timeoutMs,\n outputTail: tailLines(stdout || stderr, PROGRESS_TAIL_LINES),\n }));\n } catch {\n // 进度是纯增量呈现通道,任何失败不影响执行主流程\n }\n };\n\n /** settle 收尾统一出口:停进度计时器 */\n const finishSettled = (fn: () => void) => {\n stopProgressTimer();\n fn();\n };\n\n /** 终止态收尾:已积累输出无条件落盘 → 组装「部分输出 + 元数据块」结果。 */\n const finishTerminated = async (): Promise<ShellRunResult> => {\n const combined = [stdout, cleanForRun(stderr)].filter(Boolean).join('\\n');\n const [text, logPath] = await processOutputWithPersistence(\n combined || '',\n workdir,\n MAX_OUTPUT_LENGTH,\n true,\n );\n const meta: ShellMetadataFields = {\n terminated: true,\n reason: readTermination(),\n durationMs: Date.now() - startedAt,\n exitCode: null,\n outputBytes: Buffer.byteLength(combined, 'utf-8'),\n truncated: text.length < combined.length,\n logPath,\n };\n return {\n stdout: text,\n stderr: '',\n output: text ? `${text}\\n${formatShellMetadata(meta)}` : formatShellMetadata(meta),\n };\n };\n\n const child = spawn(opts.execPath, opts.args, {\n cwd: workdir,\n env: opts.env ?? { ...process.env },\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: true,\n // On Linux/macOS, detached: true puts the child in its own process group\n // so that process.kill(-pid) can terminate the entire group on abort.\n ...(process.platform !== 'win32' ? { detached: true } : {}),\n });\n\n const killChild = makeKillChild(child);\n\n // 执行开始:启动进度周期发射\n if (progress) {\n emitProgress();\n progressTimer = setInterval(emitProgress, PROGRESS_EMIT_INTERVAL_MS);\n progressTimer.unref?.();\n }\n\n const cleanupSignal = () => {\n signal?.removeEventListener('abort', onAbort);\n };\n\n /**\n * 终止收集(ADR-0005 中断即结果):kill → drain 到 EOF → resolve。\n * 被杀进程拿不到 exit code,元数据中记 null。\n */\n const terminateAndCollect = () => {\n if (settled) return;\n settled = true;\n cleanupSignal();\n stopProgressTimer();\n console.log(`${logPrefix} signal abort detected, killing child PID=${child.pid}`);\n killChild();\n const deadline = opts.terminationDeadline?.();\n const drainMs = Math.max(\n 0,\n (deadline ?? (Date.now() + TERMINATION_DRAIN_FALLBACK_MS)) - Date.now() - TERMINATION_DRAIN_SAFETY_MS,\n );\n void drainToEof(child, drainMs)\n .then(finishTerminated)\n .then((value) => finishSettled(() => resolve(value)))\n .catch((err) => finishSettled(() => reject(err)));\n };\n\n const onAbort = () => terminateAndCollect();\n\n if (signal) {\n if (signal.aborted) {\n // 执行前已中断:不执行命令,直接以终止态收尾\n terminateAndCollect();\n return;\n }\n signal.addEventListener('abort', onAbort, { once: true });\n }\n\n child.stdout?.on('data', (data) => {\n stdout += data.toString();\n });\n\n child.stderr?.on('data', (data) => {\n stderr += data.toString();\n });\n\n child.on('close', (code) => {\n if (settled) return;\n settled = true;\n cleanupSignal();\n stopProgressTimer();\n\n const cleanStderr = cleanForRun(stderr);\n\n if (signal?.aborted) {\n // close 在终止收集之后到达(罕见竞态):仍以终止态收尾\n void finishTerminated()\n .then((value) => finishSettled(() => resolve(value)))\n .catch((err) => finishSettled(() => reject(err)));\n return;\n }\n\n if (code === 0) {\n // 成功:stdout 和 stderr 分别独立截断(现状语义,无元数据块)\n Promise.all([\n processOutputWithPersistence(stdout || '', workdir),\n processOutputWithPersistence(cleanStderr || '', workdir),\n ]).then(([truncatedStdout, truncatedStderr]) => {\n finishSettled(() => resolve({\n stdout: truncatedStdout[0],\n stderr: truncatedStderr[0],\n output: truncatedStdout[0] || truncatedStderr[0],\n }));\n }).catch(err => {\n finishSettled(() => reject(err));\n });\n } else {\n // 失败:合并 stdout + stderr 后统一截断(现状语义)\n const combined = [stdout, cleanStderr].filter(Boolean).join('\\n\\n--- stderr ---\\n');\n processOutputWithPersistence(combined, workdir).then(([truncated]) => {\n finishSettled(() => reject(new Error(truncated || `Command failed with exit code ${code}`)));\n }).catch(err => {\n finishSettled(() => reject(err));\n });\n }\n });\n\n child.on('error', (err) => {\n if (settled) return;\n settled = true;\n cleanupSignal();\n finishSettled(() => reject(err));\n });\n });\n}\n","/**\n * PowerShell 命令执行工具\n *\n * 提供 PowerShell 路径检测、命令执行和工具定义。\n * 与 bash 工具平行,共用 shell-core.ts 的「spawn + collect + 截断落盘 +\n * 终止收集」管线(ticket 024),行为逐项一致(render 模板本就共用 bash.render.ts)。\n */\n\nimport { execSync } from 'child_process';\nimport { existsSync } from 'fs';\nimport type { Tool } from '@agentdevjs/core';\nimport { createTool } from '@agentdevjs/core';\nimport {\n runCollectedProcess,\n type ShellRunContext,\n} from './shell-core.js';\n\n// ---------------------------------------------------------------------------\n// PowerShell 路径检测\n// ---------------------------------------------------------------------------\n\nlet cachedPsPath: string | null | undefined = undefined;\n\n/**\n * 查找 PowerShell 可执行文件路径。\n *\n * 查找顺序:\n * 1. configuredPath 参数(来自 manifest 配置)\n * 2. 环境变量 AGENTDEV_POWERSHELL_PATH\n * 3. pwsh(PowerShell 7+,跨平台)\n * 4. powershell.exe(Windows PowerShell 5.1)\n * 5. Windows 已知系统路径\n *\n * 返回 null 表示未找到。\n */\nexport function findPowerShellPath(configuredPath?: string): string | null {\n if (cachedPsPath !== undefined) return cachedPsPath;\n\n // 0. 用户在 manifest 中配置的路径\n if (configuredPath && existsSync(configuredPath)) {\n cachedPsPath = configuredPath;\n return cachedPsPath;\n }\n\n // 1. 环境变量\n if (process.env.AGENTDEV_POWERSHELL_PATH && existsSync(process.env.AGENTDEV_POWERSHELL_PATH)) {\n cachedPsPath = process.env.AGENTDEV_POWERSHELL_PATH;\n return cachedPsPath;\n }\n\n const isWin = process.platform === 'win32';\n const whereCmd = isWin ? 'where' : 'which';\n\n // 2. pwsh (PowerShell Core 7+)\n try {\n const result = execSync(`${whereCmd} pwsh`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });\n const p = result.split('\\n').map(l => l.trim()).filter(Boolean)[0];\n if (p && existsSync(p)) {\n cachedPsPath = p;\n return cachedPsPath;\n }\n } catch { /* pwsh not installed */ }\n\n // 3. powershell (Windows PowerShell 5.1, Windows only)\n if (isWin) {\n try {\n const result = execSync(`${whereCmd} powershell`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });\n const p = result.split('\\n').map(l => l.trim()).filter(Boolean)[0];\n if (p && existsSync(p)) {\n cachedPsPath = p;\n return cachedPsPath;\n }\n } catch { /* not in PATH */ }\n\n // 4. Windows 系统默认路径\n const sysPath = 'C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe';\n if (existsSync(sysPath)) {\n cachedPsPath = sysPath;\n return cachedPsPath;\n }\n }\n\n cachedPsPath = null;\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// 工具定义与超时契约\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes\nconst MAX_TIMEOUT_MS = 600_000; // 10 minutes\n\nexport interface PowerShellToolOptions {\n workspaceDir?: string;\n workdir?: string;\n resourceRoot?: string;\n /** 已检测到的 PowerShell 路径 */\n psPath?: string;\n /** 覆盖默认超时(manifest 配置 defaultTimeoutMs);缺省 120000 */\n timeoutMs?: number;\n /** 覆盖超时上限(manifest 配置 maxTimeoutMs);缺省 600000 */\n maxTimeoutMs?: number;\n}\n\n/**\n * 运行 PowerShell 命令(支持 AbortSignal 中断;终止时收集部分输出并附元数据块)\n */\nexport async function runPowerShellCommand(\n command: string,\n options: PowerShellToolOptions = {},\n context?: ShellRunContext,\n): Promise<{ stdout: string; stderr: string; output: string }> {\n const workspaceDir = options.workspaceDir || process.cwd();\n const workdir = options.workdir || workspaceDir;\n const psPath = options.psPath || findPowerShellPath();\n\n if (!psPath) {\n throw new Error('PowerShell not found.');\n }\n\n console.log(`[powershell] ${command}`);\n\n return runCollectedProcess({\n workdir,\n execPath: psPath,\n args: ['-NoProfile', '-NonInteractive', '-Command', command],\n env: { ...process.env },\n logPrefix: '[powershell]',\n signal: context?.signal,\n termination: context?.termination,\n terminationDeadline: context?.terminationDeadline,\n progress: context?.progress,\n });\n}\n\nexport function createPowerShellTool(\n description: string,\n options: PowerShellToolOptions = {},\n): Tool {\n const defaultTimeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n // max 是硬上限:default 配置超过 max 时收敛到 max,保证契约自洽\n const maxTimeoutMs = Math.max(options.maxTimeoutMs ?? MAX_TIMEOUT_MS, defaultTimeoutMs, 1);\n return createTool({\n name: 'powershell',\n description,\n parameters: {\n type: 'object',\n properties: {\n command: { type: 'string' },\n timeout: { type: 'number', description: `Optional timeout in milliseconds (max ${maxTimeoutMs}). Defaults to ${defaultTimeoutMs}.` },\n },\n required: ['command'],\n },\n render: { call: 'bash', result: 'bash' },\n // 与 bash 一致:计时职责归框架 executor(ticket 023)\n timeout: {\n defaultMs: defaultTimeoutMs,\n maxMs: maxTimeoutMs,\n fromArg: 'timeout',\n },\n execute: async (args, context) => {\n const { command } = args as { command: string };\n // 生效超时(executor clamp 后)经 toolContext 透传(工单 025 进度显示)\n const effectiveTimeoutMs = typeof context?.timeoutMs === 'number' ? context.timeoutMs : null;\n const result = await runPowerShellCommand(command, options, {\n signal: context?.signal,\n termination: context?.termination,\n terminationDeadline: context?.terminationDeadline,\n progress: {\n callId: context?.callId,\n toolName: 'powershell',\n timeoutMs: effectiveTimeoutMs,\n },\n });\n return result.output;\n },\n });\n}\n","/**\n * Safe Trash 工具定义\n */\n\nimport { join } from 'path';\nimport { createTool } from '@agentdevjs/core';\nimport type { Tool } from '@agentdevjs/core';\nimport { safeRm, listTrashed, restore } from './lib/index.js';\n\nfunction formatSize(bytes: number): string {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;\n}\n\nexport function createSafeTrashDeleteTool(workspaceDir: string = process.cwd()): Tool {\n const defaultTrashDir = join(workspaceDir, '.trash');\n return createTool({\n name: 'safe_trash_delete',\n description: '安全删除文件或目录,移动到垃圾目录而非永久删除。',\n parameters: {\n type: 'object',\n properties: {\n paths: { type: 'array', items: { type: 'string' } },\n trashDir: { type: 'string' },\n },\n required: ['paths'],\n },\n render: { call: 'trash-delete', result: 'trash-delete' },\n execute: async (args) => {\n const { paths, trashDir } = args as { paths: string[]; trashDir?: string };\n const trashDirPath = trashDir || defaultTrashDir;\n const result = safeRm(workspaceDir, paths.join(' '), trashDirPath, null, 0);\n return {\n success: result.success,\n moved_count: result.movedCount,\n moved: result.moved,\n failed: result.failed,\n };\n },\n });\n}\n\nexport function createSafeTrashListTool(workspaceDir: string = process.cwd()): Tool {\n const defaultTrashDir = join(workspaceDir, '.trash');\n return createTool({\n name: 'safe_trash_list',\n description: '列出垃圾目录中的所有可恢复文件。',\n parallelizable: true,\n parameters: {\n type: 'object',\n properties: {\n trashDir: { type: 'string' },\n },\n },\n render: { call: 'trash-list', result: 'trash-list' },\n execute: async (args) => {\n const { trashDir } = args as { trashDir?: string };\n const trashDirPath = trashDir || defaultTrashDir;\n const result = listTrashed(trashDirPath, null);\n return {\n success: result.success,\n total: result.total,\n files: result.files.map((f) => ({ ...f, size_formatted: formatSize(f.size || 0) })),\n };\n },\n });\n}\n\nexport function createSafeTrashRestoreTool(workspaceDir: string = process.cwd()): Tool {\n const defaultTrashDir = join(workspaceDir, '.trash');\n return createTool({\n name: 'safe_trash_restore',\n description: '从垃圾目录恢复文件到原位置。',\n parameters: {\n type: 'object',\n properties: {\n target: {\n oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'array', items: { oneOf: [{ type: 'number' }, { type: 'string' }] } }],\n },\n trashDir: { type: 'string' },\n overwrite: { type: 'boolean' },\n },\n required: ['target'],\n },\n render: { call: 'trash-restore', result: 'trash-restore' },\n execute: async (args) => {\n const { target, trashDir, overwrite } = args as { target: number | string | number[] | string[]; trashDir?: string; overwrite?: boolean };\n const trashDirPath = trashDir || defaultTrashDir;\n const result = restore(trashDirPath, target, null, overwrite || false, false, true);\n return {\n success: result.success,\n restored_count: result.restoredCount,\n restored: result.restored,\n failed: result.failed,\n };\n },\n });\n}\n\nexport const safeTrashDeleteTool = createSafeTrashDeleteTool();\nexport const safeTrashListTool = createSafeTrashListTool();\nexport const safeTrashRestoreTool = createSafeTrashRestoreTool();\n","/**\r\n * Safe Trash 错误处理\r\n */\r\n\r\nimport { ErrorCode } from './types.js';\r\n\r\n/**\r\n * 自定义错误类\r\n */\r\nexport class SafeRmError extends Error {\r\n readonly code: ErrorCode;\r\n readonly details: Record<string, unknown>;\r\n\r\n constructor(\r\n code: ErrorCode,\r\n message: string,\r\n details?: Record<string, unknown>\r\n ) {\r\n super(message);\r\n this.name = 'SafeRmError';\r\n this.code = code;\r\n this.details = details || {};\r\n }\r\n\r\n toDict(): Record<string, unknown> {\r\n return {\r\n success: false,\r\n error_code: this.code,\r\n error_message: this.message,\r\n error_details: this.details,\r\n };\r\n }\r\n\r\n toSafeRmResult(): import('./types.js').SafeRmResult {\r\n return {\r\n success: false,\r\n moved: [],\r\n failed: [this.message],\r\n movedCount: 0,\r\n failedCount: 1,\r\n };\r\n }\r\n}\r\n\r\nexport function throwInvalidPathError(path: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_PATH, '路径无效', { path });\r\n}\r\n\r\nexport function throwPathTooLongError(path: string, length: number, max: number): never {\r\n throw new SafeRmError(ErrorCode.ERROR_PATH_TOO_LONG, `路径过长(最大 ${max} 字符)`, { path, length, max_length: max });\r\n}\r\n\r\nexport function throwIllegalCharError(path: string, char: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_PATH, `路径包含非法字符: '${char}'`, { path, illegal_char: char });\r\n}\r\n\r\nexport function throwFileNotFoundError(path: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_FILE_NOT_FOUND, '文件不存在', { path });\r\n}\r\n\r\nexport function throwPermissionDeniedError(path: string, action: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_PERMISSION_DENIED, `权限不足,无法${action}`, { path });\r\n}\r\n\r\nexport function throwSystemFileError(path: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_SYSTEM_FILE, '系统文件受保护,无法删除', { path });\r\n}\r\n\r\nexport function throwFileExistsError(path: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_FILE_EXISTS, '文件已存在', { path });\r\n}\r\n\r\nexport function throwNoTrashDirError(trashDir: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_NO_TRASH_DIR, '垃圾目录不存在', { trash_dir: trashDir });\r\n}\r\n\r\nexport function throwInvalidOperatorError(operator: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, '操作者名称无效', { operator });\r\n}\r\n\r\nexport function throwNoFilesSpecifiedError(): never {\r\n throw new SafeRmError(ErrorCode.ERROR_NO_FILES_SPECIFIED, '请指定要删除的文件', {});\r\n}\r\n","/**\r\n * 文件系统工具类\r\n */\r\n\r\nimport {\r\n existsSync,\r\n lstatSync,\r\n statSync,\r\n readdirSync,\r\n mkdirSync,\r\n renameSync,\r\n rmSync,\r\n readFileSync,\r\n writeFileSync,\r\n copyFileSync,\r\n} from 'fs';\r\nimport { join, dirname, basename, normalize } from 'path';\r\nimport {\r\n SafeRmError,\r\n throwPathTooLongError,\r\n throwIllegalCharError,\r\n throwPermissionDeniedError,\r\n} from './errors.js';\r\nimport { ErrorCode } from './types.js';\r\n\r\nconst MAX_PATH = 260;\r\nconst ILLEGAL_CHARS = '<>:\"|?*';\r\n\r\nconst SYSTEM_DIRS = [\r\n process.env.SystemRoot || 'C:\\\\Windows',\r\n 'C:\\\\Windows',\r\n 'C:\\\\Program Files',\r\n 'C:\\\\Program Files (x86)',\r\n 'C:\\\\ProgramData',\r\n];\r\n\r\nexport class FileSystem {\r\n static validatePath(path: string): void {\r\n if (!path || typeof path !== 'string') {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_PATH, '路径无效', { path: String(path) });\r\n }\r\n if (path.length > MAX_PATH) {\r\n throwPathTooLongError(path, path.length, MAX_PATH);\r\n }\r\n for (const char of ILLEGAL_CHARS) {\r\n if (path.includes(char)) {\r\n const colonContext = path.length >= 2 && path[1] === ':' && char === ':';\r\n if (!colonContext) {\r\n throwIllegalCharError(path, char);\r\n }\r\n }\r\n }\r\n }\r\n\r\n static exists(path: string): boolean {\r\n return existsSync(path);\r\n }\r\n\r\n static lexists(path: string): boolean {\r\n try {\r\n lstatSync(path);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n static isdir(path: string): boolean {\r\n try {\r\n return statSync(path).isDirectory();\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n static isfile(path: string): boolean {\r\n try {\r\n return statSync(path).isFile();\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n static islink(path: string): boolean {\r\n try {\r\n return lstatSync(path).isSymbolicLink();\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n static getsize(path: string): number {\r\n try {\r\n return statSync(path).size;\r\n } catch {\r\n return 0;\r\n }\r\n }\r\n\r\n static makedirs(path: string, mode: number = 0o755): void {\r\n if (existsSync(path) && FileSystem.isdir(path)) return;\r\n try {\r\n mkdirSync(path, { mode, recursive: true });\r\n } catch (error: any) {\r\n if (error.code === 'EACCES' || error.code === 'EPERM') {\r\n throwPermissionDeniedError(path, '创建目录');\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_CREATE_PARENT_FAILED, `无法创建目录: ${error.message}`, { path, os_error: error.code });\r\n }\r\n }\r\n\r\n static move(src: string, dst: string): void {\r\n try {\r\n const dstDir = dirname(dst);\r\n if (!existsSync(dstDir)) {\r\n this.makedirs(dstDir);\r\n }\r\n renameSync(src, dst);\r\n } catch (error: any) {\r\n if (error.code === 'EXDEV' || error.code === 'ENOENT') {\r\n try {\r\n this._copyAndDelete(src, dst);\r\n } catch (copyError: any) {\r\n if (copyError.code === 'EACCES' || copyError.code === 'EPERM') {\r\n throw new SafeRmError(ErrorCode.ERROR_FILE_LOCKED, '文件被占用或无权限访问', { source: src, destination: dst });\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `跨卷移动文件失败: ${copyError.message}`, { source: src, destination: dst });\r\n }\r\n return;\r\n }\r\n if (error.code === 'EACCES' || error.code === 'EPERM') {\r\n throw new SafeRmError(ErrorCode.ERROR_FILE_LOCKED, '文件被占用或无权限访问', { source: src, destination: dst });\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `移动文件失败: ${error.message}`, { source: src, destination: dst });\r\n }\r\n }\r\n\r\n private static _copyAndDelete(src: string, dst: string): void {\r\n const srcStat = statSync(src);\r\n if (srcStat.isDirectory()) {\r\n if (!existsSync(dst)) {\r\n mkdirSync(dst, { recursive: true });\r\n }\r\n const entries = readdirSync(src, { withFileTypes: true });\r\n for (const entry of entries) {\r\n const srcPath = join(src, entry.name);\r\n const dstPath = join(dst, entry.name);\r\n this._copyAndDelete(srcPath, dstPath);\r\n }\r\n rmSync(src, { recursive: true, force: true });\r\n } else {\r\n copyFileSync(src, dst);\r\n rmSync(src, { force: true });\r\n }\r\n }\r\n\r\n static remove(path: string): void {\r\n try {\r\n if (FileSystem.isdir(path)) {\r\n rmSync(path, { recursive: true, force: true });\r\n } else {\r\n rmSync(path, { force: true });\r\n }\r\n } catch (error: any) {\r\n if (error.code === 'EACCES' || error.code === 'EPERM') {\r\n throwPermissionDeniedError(path, '删除');\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `删除失败: ${error.message}`, { path });\r\n }\r\n }\r\n\r\n static isSystemFile(path: string): boolean {\r\n try {\r\n const absPath = normalize(path).toLowerCase();\r\n for (const sysDir of SYSTEM_DIRS) {\r\n if (sysDir && absPath.startsWith(normalize(sysDir).toLowerCase())) {\r\n return true;\r\n }\r\n }\r\n } catch {}\r\n return false;\r\n }\r\n\r\n static readFile(path: string): string {\r\n try {\r\n return readFileSync(path, 'utf-8');\r\n } catch (error: any) {\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `读取文件失败: ${error.message}`, { path });\r\n }\r\n }\r\n\r\n static writeFile(path: string, content: string): void {\r\n try {\r\n const dir = dirname(path);\r\n if (!existsSync(dir)) {\r\n this.makedirs(dir);\r\n }\r\n writeFileSync(path, content, 'utf-8');\r\n } catch (error: any) {\r\n if (error.code === 'EACCES' || error.code === 'EPERM') {\r\n throwPermissionDeniedError(path, '写入');\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `写入文件失败: ${error.message}`, { path });\r\n }\r\n }\r\n\r\n static listDir(path: string): string[] {\r\n try {\r\n return readdirSync(path);\r\n } catch (error: any) {\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `列出目录失败: ${error.message}`, { path });\r\n }\r\n }\r\n\r\n static normalizePath(path: string): string {\r\n return normalize(path).replace(/\\\\/g, '/');\r\n }\r\n\r\n static shouldSkipBySpecs(path: string): boolean {\r\n const base = basename(path);\r\n return base === '.' || base === '..';\r\n }\r\n\r\n static getFileSize(path: string): number {\r\n if (!existsSync(path)) return 0;\r\n if (FileSystem.isfile(path)) return FileSystem.getsize(path);\r\n if (FileSystem.isdir(path)) {\r\n let total = 0;\r\n try {\r\n const entries = readdirSync(path, { withFileTypes: true });\r\n for (const entry of entries) {\r\n const fullPath = join(path, entry.name);\r\n try {\r\n if (entry.isDirectory()) {\r\n total += this.getFileSize(fullPath);\r\n } else if (entry.isFile()) {\r\n total += FileSystem.getsize(fullPath);\r\n }\r\n } catch {}\r\n }\r\n } catch {}\r\n return total;\r\n }\r\n return 0;\r\n }\r\n}\r\n","/**\r\n * TrashInfo 文件解析和处理\r\n */\r\n\r\nimport { join, basename } from 'path';\r\nimport { FileSystem } from './fs.js';\r\nimport { ErrorCode, type TrashedFileInfo } from './types.js';\r\nimport { SafeRmError } from './errors.js';\r\n\r\nexport function validateOperator(operator: string | null | undefined): void {\r\n if (operator === null || operator === undefined) return;\r\n if (typeof operator !== 'string') {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, '操作者名称必须是字符串');\r\n }\r\n if (!operator) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, '操作者名称不能为空');\r\n }\r\n const illegalChars = '<>:\"|?*\\\\/()[]{}';\r\n for (const char of illegalChars) {\r\n if (operator.includes(char)) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, `操作者名称包含非法字符: '${char}'`, { operator, illegal_char: char });\r\n }\r\n }\r\n if (operator.length > 64) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, '操作者名称过长(最大 64 字符)', { operator, length: operator.length });\r\n }\r\n}\r\n\r\nexport function encodePath(path: string): string {\r\n const normalized = FileSystem.normalizePath(path);\r\n return encodeURIComponent(normalized).replace(/%2F/g, '/');\r\n}\r\n\r\nexport function decodePath(encoded: string): string {\r\n try {\r\n return decodeURIComponent(encoded);\r\n } catch {\r\n return encoded;\r\n }\r\n}\r\n\r\nexport function generateTrashName(originalPath: string): string {\r\n const base = basename(originalPath);\r\n const extIndex = base.lastIndexOf('.');\r\n let name: string;\r\n let ext: string;\r\n\r\n if (extIndex > 0) {\r\n name = base.substring(0, extIndex);\r\n ext = base.substring(extIndex);\r\n } else {\r\n name = base;\r\n ext = '';\r\n }\r\n\r\n const now = new Date();\r\n const timestamp = now.getFullYear().toString() +\r\n (now.getMonth() + 1).toString().padStart(2, '0') +\r\n now.getDate().toString().padStart(2, '0') +\r\n now.getHours().toString().padStart(2, '0') +\r\n now.getMinutes().toString().padStart(2, '0') +\r\n now.getSeconds().toString().padStart(2, '0');\r\n\r\n const randomSuffix = Math.floor(Math.random() * 9000) + 1000;\r\n\r\n return `${name}_${timestamp}_${randomSuffix}${ext}`;\r\n}\r\n\r\nexport function createTrashInfo(\r\n originalPath: string,\r\n trashName: string,\r\n trashDir: string,\r\n operator: string | null\r\n): string {\r\n const infoDir = join(trashDir, 'info');\r\n const infoPath = join(infoDir, `${trashName}.trashinfo`);\r\n const encodedPath = encodePath(originalPath);\r\n const now = new Date();\r\n const deletionDate = now.toISOString().replace(/\\.\\d{3}Z$/, '');\r\n\r\n let content = `[Trash Info]\r\nPath=${encodedPath}\r\nDeletionDate=${deletionDate}\r\n`;\r\n if (operator) {\r\n content += `Operator=${operator}\\n`;\r\n }\r\n\r\n FileSystem.writeFile(infoPath, content);\r\n return infoPath;\r\n}\r\n\r\nexport function parseTrashInfo(infoPath: string, trashDir: string): TrashedFileInfo | null {\r\n try {\r\n const content = FileSystem.readFile(infoPath);\r\n let originalPath: string | null = null;\r\n let deletionDate: string | null = null;\r\n let operator: string | null = null;\r\n\r\n for (const line of content.split('\\n')) {\r\n const trimmed = line.trim();\r\n if (trimmed.startsWith('Path=')) {\r\n originalPath = decodePath(trimmed.substring(5));\r\n } else if (trimmed.startsWith('DeletionDate=')) {\r\n deletionDate = trimmed.substring(13);\r\n } else if (trimmed.startsWith('Operator=')) {\r\n operator = trimmed.substring(9);\r\n }\r\n }\r\n\r\n if (!originalPath || !deletionDate) return null;\r\n\r\n try {\r\n FileSystem.validatePath(originalPath);\r\n } catch {\r\n return null;\r\n }\r\n\r\n const infoBasename = basename(infoPath);\r\n const trashBasename = infoBasename.replace(/\\.trashinfo$/, '');\r\n const trashFile = join(trashDir, 'files', trashBasename);\r\n\r\n if (!FileSystem.exists(trashFile)) return null;\r\n\r\n const size = FileSystem.getFileSize(trashFile);\r\n\r\n return {\r\n index: -1,\r\n originalPath,\r\n deletionDate,\r\n operator,\r\n trashFile,\r\n infoFile: infoPath,\r\n size,\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport function parseRmCommand(rmCommand: string): {\r\n files: string[];\r\n force: boolean;\r\n interactive: boolean;\r\n recursive: boolean;\r\n} {\r\n const trimmed = rmCommand.trim();\r\n if (!trimmed) {\r\n return { files: [], force: false, interactive: false, recursive: false };\r\n }\r\n\r\n const parts: string[] = [];\r\n let current = '';\r\n let inQuote = false;\r\n let quoteChar = '';\r\n\r\n for (let i = 0; i < trimmed.length; i++) {\r\n const char = trimmed[i];\r\n const nextChar = trimmed[i + 1] || '';\r\n\r\n if (inQuote) {\r\n if (char === quoteChar) {\r\n if (nextChar === quoteChar) {\r\n current += char;\r\n i++;\r\n } else {\r\n inQuote = false;\r\n }\r\n } else {\r\n current += char;\r\n }\r\n } else if (char === '\"' || char === \"'\" || char === '`') {\r\n inQuote = true;\r\n quoteChar = char;\r\n } else if (char === ' ' || char === '\\t') {\r\n if (current) {\r\n parts.push(current);\r\n current = '';\r\n }\r\n } else {\r\n current += char;\r\n }\r\n }\r\n\r\n if (current) parts.push(current);\r\n\r\n const files: string[] = [];\r\n let force = false;\r\n let interactive = false;\r\n let recursive = false;\r\n\r\n for (let part of parts) {\r\n if ((part.startsWith('\"') && part.endsWith('\"')) ||\r\n (part.startsWith(\"'\") && part.endsWith(\"'\")) ||\r\n (part.startsWith('`') && part.endsWith('`'))) {\r\n part = part.slice(1, -1);\r\n }\r\n if (!part) continue;\r\n if (part.startsWith('-')) {\r\n if (part.includes('f') || part === '--force') force = true;\r\n if (part.includes('i') || part === '--interactive') interactive = true;\r\n if (part.includes('r') || part.includes('R') || part === '--recursive') recursive = true;\r\n } else {\r\n files.push(part);\r\n }\r\n }\r\n\r\n return { files, force, interactive, recursive };\r\n}\r\n","/**\r\n * SafeRm - 安全删除核心逻辑\r\n */\r\n\r\nimport { isAbsolute, join, resolve } from 'path';\nimport { FileSystem } from './fs.js';\nimport { validateOperator, generateTrashName, createTrashInfo, parseRmCommand } from './trashinfo.js';\r\nimport {\r\n SafeRmError,\r\n throwNoFilesSpecifiedError,\r\n throwFileNotFoundError,\r\n} from './errors.js';\r\nimport { ErrorCode, Mode, TrashResult, type SafeRmOptions, type SafeRmResult } from './types.js';\r\n\r\nexport class SafeRm {\r\n readonly trashDir: string;\r\n readonly workingDir: string | null;\r\n readonly operator: string | null;\r\n readonly verbose: number;\r\n\r\n constructor(options: SafeRmOptions) {\r\n this.trashDir = resolve(options.trashDir);\r\n this.workingDir = options.workingDir || null;\r\n this.operator = options.operator || null;\r\n this.verbose = options.verbose || 0;\r\n validateOperator(this.operator);\r\n this._ensureTrashDir();\r\n }\r\n\r\n private _ensureTrashDir(): void {\r\n const parentDir = join(this.trashDir, '..');\r\n if (!FileSystem.exists(parentDir)) {\r\n throw new SafeRmError(ErrorCode.ERROR_NO_TRASH_DIR, '垃圾目录的父目录不存在', { trash_dir: this.trashDir, parent_dir: parentDir });\r\n }\r\n FileSystem.makedirs(this.trashDir);\r\n FileSystem.makedirs(join(this.trashDir, 'info'));\r\n FileSystem.makedirs(join(this.trashDir, 'files'));\r\n\r\n const testFile = join(this.trashDir, 'info', '.write_test');\r\n try {\r\n FileSystem.writeFile(testFile, 'test');\r\n FileSystem.remove(testFile);\r\n } catch {\r\n throw new SafeRmError(ErrorCode.ERROR_TRASH_NOT_WRITABLE, '垃圾目录不可写', { trash_dir: this.trashDir });\r\n }\r\n }\r\n\r\n trashSingle(path: string, mode: Mode): TrashResult {\r\n FileSystem.validatePath(path);\r\n if (FileSystem.shouldSkipBySpecs(path)) return TrashResult.FAILURE;\r\n\r\n const absPath = resolve(path);\r\n if (FileSystem.isSystemFile(absPath)) {\r\n throw new SafeRmError(ErrorCode.ERROR_SYSTEM_FILE, '系统文件受保护,无法删除', { path });\r\n }\r\n\r\n if (!FileSystem.lexists(path)) {\r\n if (mode === Mode.MODE_FORCE) return TrashResult.SUCCESS;\r\n throwFileNotFoundError(path);\r\n }\r\n\r\n try {\r\n const trashName = generateTrashName(path);\r\n const destPath = join(this.trashDir, 'files', trashName);\r\n createTrashInfo(path, trashName, this.trashDir, this.operator);\r\n FileSystem.move(path, destPath);\r\n return TrashResult.SUCCESS;\r\n } catch (error) {\r\n if (error instanceof SafeRmError) throw error;\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `删除文件失败: ${error instanceof Error ? error.message : String(error)}`, { path });\r\n }\r\n }\r\n\r\n trashAll(paths: string[], mode: Mode): SafeRmResult {\r\n const failedPaths: string[] = [];\r\n const movedPaths: string[] = [];\r\n\r\n for (const path of paths) {\r\n try {\r\n const result = this.trashSingle(path, mode);\r\n if (result === TrashResult.SUCCESS) {\r\n movedPaths.push(path);\r\n } else {\r\n failedPaths.push(path);\r\n }\r\n } catch {\r\n failedPaths.push(path);\r\n }\r\n }\r\n\r\n return {\r\n success: failedPaths.length === 0,\r\n moved: movedPaths,\r\n failed: failedPaths,\r\n movedCount: movedPaths.length,\r\n failedCount: failedPaths.length,\r\n };\r\n }\r\n}\r\n\r\nexport function safeRm(\r\n workingDir: string | null,\r\n rmCommand: string,\r\n trashDir: string,\r\n operator: string | null = null,\r\n verbose: number = 0\r\n): SafeRmResult {\r\n validateOperator(operator);\r\n const { files: parsedFiles, force, interactive } = parseRmCommand(rmCommand);\n const files = parsedFiles.map((file) => {\n if (!workingDir || isAbsolute(file)) {\n return file;\n }\n return resolve(workingDir, file);\n });\n\r\n if (files.length === 0) throwNoFilesSpecifiedError();\r\n\r\n let mode: Mode;\r\n if (force) mode = Mode.MODE_FORCE;\r\n else if (interactive) mode = Mode.MODE_INTERACTIVE;\r\n else mode = Mode.MODE_UNSPECIFIED;\r\n\r\n if (workingDir) {\n if (!FileSystem.exists(workingDir)) {\n throw new SafeRmError(ErrorCode.ERROR_WORKING_DIR_NOT_FOUND, '工作目录不存在', { working_dir: workingDir });\n }\n }\n\n const safeRmInstance = new SafeRm({ trashDir, workingDir, operator, verbose });\n return safeRmInstance.trashAll(files, mode);\n}\n","/**\r\n * SafeRestore - 安全恢复核心逻辑\r\n */\r\n\r\nimport { join, resolve, dirname } from 'path';\r\nimport { FileSystem } from './fs.js';\r\nimport { validateOperator, parseTrashInfo } from './trashinfo.js';\r\nimport { SafeRmError, throwNoTrashDirError } from './errors.js';\r\nimport { ErrorCode, type RestoreOptions, type RestoreTarget, type RestoreResult, type ListResult, type TrashedFileInfo } from './types.js';\r\n\r\nfunction matchesPattern(actualPath: string, pattern: string): boolean {\r\n const regexPattern = pattern\r\n .replace(/\\./g, '\\\\.')\r\n .replace(/\\*/g, '.*')\r\n .replace(/\\?/g, '.');\r\n const regex = new RegExp(`^${regexPattern}$`, 'i');\r\n return regex.test(actualPath) || regex.test(actualPath.replace(/\\//g, '\\\\'));\r\n}\r\n\r\nfunction parseIndexSpec(spec: string): number[] {\r\n const indices: number[] = [];\r\n for (const part of spec.split(',')) {\r\n const trimmed = part.trim();\r\n if (trimmed.includes('-')) {\r\n const [startStr, endStr] = trimmed.split('-', 2);\r\n const start = parseInt(startStr, 10);\r\n const end = parseInt(endStr, 10);\r\n if (isNaN(start) || isNaN(end)) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_INDEX, `无效的索引范围: ${trimmed}`, { spec });\r\n }\r\n for (let i = start; i <= end; i++) indices.push(i);\r\n } else {\r\n const index = parseInt(trimmed, 10);\r\n if (isNaN(index)) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_INDEX, `无效的索引: ${trimmed}`, { spec });\r\n }\r\n indices.push(index);\r\n }\r\n }\r\n return indices;\r\n}\r\n\r\nexport class SafeRestore {\r\n readonly trashDir: string;\r\n readonly operator: string | null;\r\n readonly verbose: number;\r\n\r\n constructor(options: RestoreOptions) {\r\n this.trashDir = resolve(options.trashDir);\r\n this.operator = options.operator || null;\r\n this.verbose = options.verbose || 0;\r\n validateOperator(this.operator);\r\n if (!FileSystem.exists(this.trashDir)) throwNoTrashDirError(this.trashDir);\r\n }\r\n\r\n private _scanInfoFiles(): TrashedFileInfo[] {\r\n const infoDir = join(this.trashDir, 'info');\r\n const files: TrashedFileInfo[] = [];\r\n if (!FileSystem.exists(infoDir)) return files;\r\n\r\n const entries = FileSystem.listDir(infoDir);\r\n for (const filename of entries) {\r\n if (!filename.endsWith('.trashinfo')) continue;\r\n const infoPath = join(infoDir, filename);\r\n const info = parseTrashInfo(infoPath, this.trashDir);\r\n if (info && (this.operator === null || info.operator === this.operator)) {\r\n files.push(info);\r\n }\r\n }\r\n return files;\r\n }\r\n\r\n listTrashed(): TrashedFileInfo[] {\r\n const files = this._scanInfoFiles();\r\n files.sort((a, b) => a.deletionDate.localeCompare(b.deletionDate));\r\n for (let i = 0; i < files.length; i++) files[i] = { ...files[i], index: i };\r\n return files;\r\n }\r\n\r\n private _matchesTarget(info: TrashedFileInfo, target: number | string): boolean {\r\n if (typeof target === 'number') return info.index === target;\r\n return matchesPattern(info.originalPath, target) || matchesPattern(info.originalPath.replace(/\\//g, '\\\\'), target);\r\n }\r\n\r\n restore(target: RestoreTarget, overwrite: boolean = false, dryRun: boolean = false): RestoreResult {\r\n const allFiles = this.listTrashed();\r\n const restored: string[] = [];\r\n const failed: Array<{ path: string; error: string }> = [];\r\n const skipped: string[] = [];\r\n\r\n const targets: Array<number | string> = Array.isArray(target) ? target : [target];\r\n const filesToRestore = new Set<TrashedFileInfo>();\r\n\r\n for (const t of targets) {\r\n let matched = false;\r\n for (const info of allFiles) {\r\n if (this._matchesTarget(info, t)) {\r\n filesToRestore.add(info);\r\n matched = true;\r\n }\r\n }\r\n if (!matched && typeof t === 'number') {\r\n failed.push({ path: `index:${t}`, error: '索引没有找到对应的文件' });\r\n }\r\n }\r\n\r\n for (const info of Array.from(filesToRestore)) {\r\n try {\r\n if (FileSystem.exists(info.originalPath) && !overwrite) {\r\n failed.push({ path: info.originalPath, error: 'file already exists' });\r\n continue;\r\n }\r\n const parentDir = dirname(info.originalPath);\r\n if (parentDir && !FileSystem.exists(parentDir)) FileSystem.makedirs(parentDir);\r\n\r\n if (dryRun) {\r\n restored.push(info.originalPath);\r\n } else {\r\n FileSystem.move(info.trashFile, info.originalPath);\r\n FileSystem.remove(info.infoFile);\r\n restored.push(info.originalPath);\r\n }\r\n } catch (error) {\r\n failed.push({ path: info.originalPath, error: error instanceof Error ? error.message : String(error) });\r\n }\r\n }\r\n\r\n return {\r\n success: failed.length === 0,\r\n restored,\r\n failed,\r\n skipped,\r\n restoredCount: restored.length,\r\n failedCount: failed.length,\r\n skippedCount: skipped.length,\r\n };\r\n }\r\n}\r\n\r\nexport function listTrashed(trashDir: string, operator: string | null = null): ListResult {\r\n validateOperator(operator);\r\n const restorer = new SafeRestore({ trashDir, operator });\r\n const infoList = restorer.listTrashed();\r\n return { success: true, total: infoList.length, files: infoList };\r\n}\r\n\r\nexport function restore(\r\n trashDir: string,\r\n target: RestoreTarget,\r\n operator: string | null = null,\r\n overwrite: boolean = false,\r\n dryRun: boolean = false,\r\n parseIndexRanges: boolean = true\r\n): RestoreResult {\r\n validateOperator(operator);\r\n const restorer = new SafeRestore({ trashDir, operator });\r\n\r\n let actualTarget: RestoreTarget = target;\r\n if (parseIndexRanges && typeof target === 'string') {\r\n if (/^[\\d,\\s\\-]+$/.test(target.trim())) {\r\n try {\r\n actualTarget = parseIndexSpec(target);\r\n } catch {\r\n actualTarget = target;\r\n }\r\n }\r\n }\r\n\r\n return restorer.restore(actualTarget, overwrite, dryRun);\r\n}\r\n"],"mappings":"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","names":["resolve","path","quoted","resolve","execSync","existsSync","createTool","existsSync","execSync","DEFAULT_TIMEOUT_MS","MAX_TIMEOUT_MS","createTool","join","createTool","path","existsSync","join","dirname","path","existsSync","dirname","join","join","basename","path","basename","join","join","join","path","join","resolve","dirname","resolve","join","dirname","join","createTool","DEFAULT_TIMEOUT_MS","MAX_TIMEOUT_MS","resolve"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/tools.ts","../src/shellQuoting.ts","../src/shell-core.ts","../src/powershell.ts","../src/tools-trash.ts","../src/lib/errors.ts","../src/lib/fs.ts","../src/lib/trashinfo.ts","../src/lib/trash.ts","../src/lib/restore.ts"],"sourcesContent":["/**\r\n * Shell Feature - 独立 npm 包\r\n *\r\n * 支持 Bash(Windows: Git Bash / Linux/macOS: 原生 bash)和 PowerShell 两种 Shell 环境。\r\n * 根据用户配置和运行时探测结果,条件注册 Bash 和/或 PowerShell 工具。\r\n *\r\n * @example\r\n * ```typescript\r\n * import { ShellFeature } from '@agentdevjs/shell-feature';\r\n * import { BasicAgent } from '@agentdevjs/core';\r\n *\r\n * const agent = new BasicAgent().use(new ShellFeature());\r\n * ```\r\n */\r\n\r\nimport { readFile } from 'fs/promises';\r\nimport { fileURLToPath } from 'url';\r\nimport { resolve } from 'path';\r\nimport type { AgentFeature, FeatureInitContext, FeatureManifestDefinition, PackageInfo } from '@agentdevjs/core';\r\nimport type { Tool } from '@agentdevjs/core';\r\nimport { getPackageInfoFromSource } from '@agentdevjs/core';\r\nimport { createShellCommandTool, findGitBashPath } from './tools.js';\r\nimport { createPowerShellTool, findPowerShellPath } from './powershell.js';\r\nimport { createSafeTrashDeleteTool, createSafeTrashListTool, createSafeTrashRestoreTool } from './tools-trash.js';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\n\r\nexport interface ShellFeatureConfig {\r\n workspaceDir?: string;\r\n workdir?: string;\r\n resourceRoot?: string;\r\n}\r\n\r\ninterface ResolvedShellConfig {\r\n bashEnabled: boolean;\r\n bashPath?: string;\r\n powershellEnabled: boolean;\r\n powershellPath?: string;\r\n /** 默认命令超时(毫秒),executor 计时起点;默认 120000 */\r\n defaultTimeoutMs: number;\r\n /** 超时硬上限(毫秒),任何来源的超时值 clamp 到此值;默认 600000 */\r\n maxTimeoutMs: number;\r\n}\r\n\r\n/** 默认超时契约值(与工具 timeout 声明保持一致,ticket 024 步骤 5)。 */\r\nconst DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes\r\nconst MAX_TIMEOUT_MS = 600_000; // 10 minutes\r\n\r\nfunction resolvePositiveNumber(value: unknown, fallback: number): number {\r\n return typeof value === 'number' && Number.isFinite(value) && value > 0\r\n ? Math.floor(value)\r\n : fallback;\r\n}\r\n\r\n/**\r\n * Shell Feature 实现\r\n */\r\nexport class ShellFeature implements AgentFeature {\r\n readonly name = 'shell';\r\n readonly dependencies: string[] = [];\r\n readonly source = __filename.replace(/\\\\/g, '/');\r\n readonly description = '提供 Bash/PowerShell 命令执行能力,以及安全删除、恢复和查看垃圾桶工具。';\r\n\r\n private bashDescription?: string;\r\n private powershellDescription?: string;\r\n private _packageInfo: PackageInfo | null = null;\r\n private readonly workspaceDir: string;\r\n private readonly workdir: string;\r\n private readonly resourceRoot: string;\r\n\r\n constructor(config: ShellFeatureConfig = {}) {\r\n this.workspaceDir = config.workspaceDir || process.cwd();\r\n this.workdir = config.workdir || this.workspaceDir;\r\n this.resourceRoot = config.resourceRoot || process.cwd();\r\n }\r\n\r\n /**\r\n * 获取同步工具(垃圾桶工具)\r\n */\r\n getTools(): Tool[] {\r\n return [\r\n createSafeTrashDeleteTool(this.workdir),\r\n createSafeTrashListTool(this.workdir),\r\n createSafeTrashRestoreTool(this.workdir),\r\n ];\r\n }\r\n\r\n getFeatureManifest(): FeatureManifestDefinition {\r\n return {\r\n schemaVersion: 1 as const,\r\n settings: {\r\n properties: {\r\n bashEnabled: {\r\n type: 'boolean',\r\n title: '启用 Bash',\r\n description: '启用后,Agent 将获得 Bash 工具。Windows 需要 Git for Windows;Linux/macOS 使用系统自带 Shell。',\r\n default: true,\r\n },\r\n bashPath: {\r\n type: 'file',\r\n title: 'Bash 路径',\r\n description: 'Bash 可执行文件路径。留空时自动检测。',\r\n placeholder: '自动检测',\r\n },\r\n powershellEnabled: {\r\n type: 'boolean',\r\n title: '启用 PowerShell',\r\n description: '启用后,Agent 将获得 PowerShell 工具。Windows 自带 PowerShell 5.1;Linux/macOS 需安装 PowerShell Core (pwsh)。',\r\n default: true,\r\n },\r\n powershellPath: {\r\n type: 'file',\r\n title: 'PowerShell 路径',\r\n description: 'PowerShell 可执行文件路径。留空时自动检测。',\r\n placeholder: '自动检测',\r\n },\r\n defaultTimeoutMs: {\r\n type: 'number',\r\n title: '默认命令超时(毫秒)',\r\n description: '命令执行的默认超时时间。模型可通过 timeout 参数覆盖(不超过最大超时)。',\r\n default: DEFAULT_TIMEOUT_MS,\r\n min: 1,\r\n max: MAX_TIMEOUT_MS,\r\n step: 1000,\r\n },\r\n maxTimeoutMs: {\r\n type: 'number',\r\n title: '最大命令超时(毫秒)',\r\n description: '命令超时的硬上限,任何来源的超时值都会被限制到此值以内。',\r\n default: MAX_TIMEOUT_MS,\r\n min: 1,\r\n step: 1000,\r\n },\r\n },\r\n },\r\n };\r\n }\r\n\r\n private resolveShellConfig(featureConfig: unknown): ResolvedShellConfig {\r\n if (!featureConfig || typeof featureConfig !== 'object') {\r\n return {\r\n bashEnabled: true,\r\n powershellEnabled: true,\r\n defaultTimeoutMs: DEFAULT_TIMEOUT_MS,\r\n maxTimeoutMs: MAX_TIMEOUT_MS,\r\n };\r\n }\r\n const c = featureConfig as Record<string, unknown>;\r\n const maxTimeoutMs = Math.max(\r\n resolvePositiveNumber(c.maxTimeoutMs, MAX_TIMEOUT_MS),\r\n 1,\r\n );\r\n return {\r\n bashEnabled: c.bashEnabled !== false,\r\n bashPath: typeof c.bashPath === 'string' && c.bashPath.trim() ? c.bashPath.trim() : undefined,\r\n powershellEnabled: c.powershellEnabled !== false,\r\n powershellPath: typeof c.powershellPath === 'string' && c.powershellPath.trim() ? c.powershellPath.trim() : undefined,\r\n // default 不允许超过 max(配置面板顺序无关时的自洽保护)\r\n defaultTimeoutMs: Math.min(\r\n resolvePositiveNumber(c.defaultTimeoutMs, DEFAULT_TIMEOUT_MS),\r\n maxTimeoutMs,\r\n ),\r\n maxTimeoutMs,\r\n };\r\n }\r\n\r\n /**\r\n * 获取异步工具(bash/powershell 工具,条件注册)\r\n */\r\n async getAsyncTools(ctx: FeatureInitContext): Promise<Tool[]> {\r\n const config = this.resolveShellConfig(ctx.featureConfig);\r\n const tools: Tool[] = [];\r\n\r\n // ── Bash 工具 ──\r\n if (config.bashEnabled) {\r\n const bashPath = findGitBashPath(config.bashPath);\r\n if (bashPath) {\r\n if (!this.bashDescription) {\r\n try {\r\n const descriptionPath = resolve(this.resourceRoot, '.agentdev/prompts/tool-bash.md');\r\n this.bashDescription = await readFile(descriptionPath, 'utf-8');\r\n } catch {\r\n this.bashDescription = '执行 Shell 命令';\r\n }\r\n }\r\n tools.push(createShellCommandTool(this.bashDescription, {\r\n workspaceDir: this.workspaceDir,\r\n workdir: this.workdir,\r\n resourceRoot: this.resourceRoot,\r\n bashPath,\r\n timeoutMs: config.defaultTimeoutMs,\r\n maxTimeoutMs: config.maxTimeoutMs,\r\n }));\r\n } else {\r\n console.warn('[shell] Bash is enabled but was not found on this system. Skipping Bash tool.');\r\n }\r\n }\r\n\r\n // ── PowerShell 工具 ──\r\n if (config.powershellEnabled) {\r\n const psPath = findPowerShellPath(config.powershellPath);\r\n if (psPath) {\r\n if (!this.powershellDescription) {\r\n try {\r\n const descriptionPath = resolve(this.resourceRoot, '.agentdev/prompts/tool-powershell.md');\r\n this.powershellDescription = await readFile(descriptionPath, 'utf-8');\r\n } catch {\r\n this.powershellDescription = '执行 PowerShell 命令';\r\n }\r\n }\r\n tools.push(createPowerShellTool(this.powershellDescription, {\r\n workspaceDir: this.workspaceDir,\r\n workdir: this.workdir,\r\n resourceRoot: this.resourceRoot,\r\n psPath,\r\n timeoutMs: config.defaultTimeoutMs,\r\n maxTimeoutMs: config.maxTimeoutMs,\r\n }));\r\n } else {\r\n console.warn('[shell] PowerShell is enabled but was not found on this system. Skipping PowerShell tool.');\r\n }\r\n }\r\n\r\n return tools;\r\n }\r\n\r\n /**\r\n * 获取包信息(统一打包方案)\r\n */\r\n getPackageInfo(): PackageInfo | null {\r\n if (!this._packageInfo) {\r\n this._packageInfo = getPackageInfoFromSource(this.source);\r\n }\r\n return this._packageInfo;\r\n }\r\n\r\n /**\r\n * 获取模板名称列表(统一打包方案)\r\n */\r\n getTemplateNames(): string[] {\r\n return [\r\n 'bash',\r\n 'trash-delete',\r\n 'trash-list',\r\n 'trash-restore',\r\n ];\r\n }\r\n}\r\n\r\n// 导出工具创建函数(供高级用户使用)\r\nexport { createShellCommandTool, runShellCommand, findGitBashPath } from './tools.js';\r\nexport type { ShellCommandToolOptions, ShellExecutionResult } from './tools.js';\r\nexport { createPowerShellTool, runPowerShellCommand, findPowerShellPath } from './powershell.js';\r\n\r\n// 导出共享运行核心(供高级用户使用;截断落盘函数自 tools.ts 迁入,行为不变)\r\nexport {\r\n processOutputWithPersistence,\r\n formatShellMetadata,\r\n SHELL_METADATA_OPEN,\r\n SHELL_METADATA_CLOSE,\r\n} from './shell-core.js';\r\nexport type {\r\n ShellRunResult,\r\n ShellMetadataFields,\r\n} from './shell-core.js';\r\n\r\n// 导出命令引用工具(供高级用户使用)\r\nexport {\r\n quoteShellCommand,\r\n shouldAddStdinRedirect,\r\n rewriteWindowsNullRedirect,\r\n containsHeredoc,\r\n hasStdinRedirect,\r\n} from './shellQuoting.js';\r\nexport {\r\n safeTrashDeleteTool,\r\n safeTrashListTool,\r\n safeTrashRestoreTool,\r\n createSafeTrashDeleteTool,\r\n createSafeTrashListTool,\r\n createSafeTrashRestoreTool,\r\n} from './tools-trash.js';\r\n\r\n// 导出库函数(供高级用户使用)\r\nexport { safeRm, listTrashed, restore } from './lib/index.js';\r\n","/**\r\n * Shell Feature 工具定义\r\n *\r\n * 提供 bash 工具,在 Windows 上通过 Git Bash、在 Linux/macOS 上通过原生 bash 执行 Shell 命令,支持 AbortSignal 中断。\r\n *\r\n * 改进点(照搬 Claude Code 的优秀实践):\r\n * 1. 命令引用:eval + 单引号包裹,彻底解决 syntax error near unexpected token '('\r\n * 2. 非 -i 模式:去掉 interactive flag,消除 job control 警告\r\n * 3. stdin redirect:自动添加 < /dev/null 防止命令挂起\r\n * 4. Windows null rewrite:>nul → >/dev/null\r\n * 5. 动态 bash 路径检测(Windows: Git Bash; Linux/macOS: $SHELL || /bin/bash)\r\n * 6. 输出截断:防止大输出撑爆 LLM 上下文\r\n *\r\n * 终止语义(ticket 024 / ADR-0005):超时计时归框架 executor(Tool.timeout\r\n * 声明契约);signal aborted 时 kill → drain 到 EOF → resolve 部分输出 +\r\n * <shell_metadata> 块(见 shell-core.ts)。\r\n */\r\n\r\nimport { execSync } from 'child_process';\r\nimport { existsSync } from 'fs';\r\nimport * as path from 'path';\r\nimport type { Tool } from '@agentdevjs/core';\r\nimport { createTool } from '@agentdevjs/core';\r\nimport {\r\n quoteShellCommand,\r\n shouldAddStdinRedirect,\r\n rewriteWindowsNullRedirect,\r\n} from './shellQuoting.js';\r\nimport {\r\n runCollectedProcess,\r\n // ShellRunContext 复用共享运行核心的上下文形状(signal/termination/progress)\r\n type ShellRunContext,\r\n} from './shell-core.js';\r\n\r\nexport interface ShellCommandToolOptions {\r\n workspaceDir?: string;\r\n workdir?: string;\r\n resourceRoot?: string;\r\n /** Override bash path detection (used when ShellFeature pre-detects the path) */\r\n bashPath?: string;\r\n /** 覆盖默认超时(manifest 配置 defaultTimeoutMs);缺省 120000 */\r\n timeoutMs?: number;\r\n /** 覆盖超时上限(manifest 配置 maxTimeoutMs);缺省 600000 */\r\n maxTimeoutMs?: number;\r\n}\r\n\r\nexport interface ShellExecutionResult {\r\n stdout: string;\r\n stderr: string;\r\n output: string;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 动态 Git Bash 路径检测(照搬 Claude Code 的 findGitBashPath)\r\n// ---------------------------------------------------------------------------\r\n\r\nlet cachedBashPath: string | null = null;\r\n\r\n/**\r\n * 动态查找 Git Bash 的 bash.exe 路径。\r\n *\r\n * 查找顺序:\r\n * 1. configuredPath 参数(来自 manifest 配置)\r\n * 2. 环境变量 AGENTDEV_GIT_BASH_PATH\r\n * 3. 环境变量 SHELL(如果包含 bash)\r\n * 4. where bash(Windows)\r\n * 5. 常见安装位置\r\n *\r\n * 返回 null 表示未找到(调用方应据此决定是否注册工具)。\r\n */\r\nexport function findGitBashPath(configuredPath?: string): string | null {\r\n if (cachedBashPath) return cachedBashPath;\r\n\r\n // 0. 用户在 manifest 中配置的路径\r\n if (configuredPath && existsSync(configuredPath)) {\r\n cachedBashPath = configuredPath;\r\n return cachedBashPath;\r\n }\r\n\r\n if (process.platform !== 'win32') {\r\n cachedBashPath = process.env.SHELL || '/bin/bash';\r\n return cachedBashPath;\r\n }\r\n\r\n const candidates: string[] = [];\r\n\r\n if (process.env.AGENTDEV_GIT_BASH_PATH) {\r\n candidates.push(process.env.AGENTDEV_GIT_BASH_PATH);\r\n }\r\n\r\n candidates.push('C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe');\r\n candidates.push('C:\\\\Program Files (x86)\\\\Git\\\\bin\\\\bash.exe');\r\n\r\n try {\r\n const result = execSync('where bash', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });\r\n for (const line of result.split('\\n').map(l => l.trim()).filter(Boolean)) {\r\n if (line.toLowerCase().includes('git')) {\r\n candidates.push(line);\r\n }\r\n }\r\n } catch {\r\n // where 命令可能不可用\r\n }\r\n\r\n try {\r\n const gitPath = execSync('where git', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).split('\\n')[0]?.trim();\r\n if (gitPath) {\r\n const derived = path.join(path.dirname(path.dirname(gitPath)), 'bin', 'bash.exe');\r\n candidates.push(derived);\r\n }\r\n } catch {\r\n // git 可能不在 PATH\r\n }\r\n\r\n for (const candidate of candidates) {\r\n if (candidate && existsSync(candidate)) {\r\n cachedBashPath = candidate;\r\n return cachedBashPath;\r\n }\r\n }\r\n\r\n cachedBashPath = null;\r\n return null;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 核心运行参数与超时契约默认值\r\n// ---------------------------------------------------------------------------\r\n\r\nconst DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes\r\nconst MAX_TIMEOUT_MS = 600_000; // 10 minutes\r\n\r\n/**\r\n * 运行 Shell 命令(支持 AbortSignal 中断;终止时收集部分输出并附元数据块)\r\n *\r\n * 关键改进:\r\n * - 使用 eval + 单引号引用替代 naive 的双引号转义\r\n * - 去掉 -i(interactive)flag\r\n * - 添加 stdin redirect\r\n * - 重写 Windows null redirect\r\n */\r\nexport async function runShellCommand(\r\n command: string,\r\n options: ShellCommandToolOptions = {},\r\n context?: ShellRunContext,\r\n): Promise<ShellExecutionResult> {\r\n const workspaceDir = options.workspaceDir || process.cwd();\r\n const workdir = options.workdir || workspaceDir;\r\n const resourceRoot = options.resourceRoot || process.cwd();\r\n const bashrcPath = resourceRoot.replace(/\\\\/g, '/') + '/.agentdev/bashrc';\r\n\r\n console.log(`[shell] ${command}`);\r\n\r\n // 1. 重写 Windows CMD 风格的 null redirect\r\n const normalizedCommand = rewriteWindowsNullRedirect(command);\r\n\r\n // 2. 安全引用命令\r\n const addStdinRedirect = shouldAddStdinRedirect(normalizedCommand);\r\n const quotedCommand = quoteShellCommand(normalizedCommand, addStdinRedirect);\r\n\r\n // 3. 构建 eval 命令字符串\r\n const quotedBashrc = `'${bashrcPath.replace(/'/g, `'\\\\''`)}'`;\r\n const commandString = `source ${quotedBashrc} 2>/dev/null || true; eval ${quotedCommand}`;\r\n\r\n // 4. 确定 bash 路径和参数\r\n const bashPath = options.bashPath || findGitBashPath();\r\n if (!bashPath) {\r\n const hint = process.platform === 'win32'\r\n ? 'Git Bash not found. Please install Git for Windows or configure the path in settings.'\r\n : 'Bash not found. Please ensure bash is installed or configure the path in settings.';\r\n throw new Error(hint);\r\n }\r\n\r\n const isWin = process.platform === 'win32';\r\n\r\n return runCollectedProcess({\r\n workdir,\r\n execPath: bashPath,\r\n args: ['-c', commandString],\r\n env: {\r\n ...process.env,\r\n // MSYSTEM is only meaningful for Git Bash (MSYS2/MinGW) on Windows.\r\n ...(isWin ? { MSYSTEM: process.env.MSYSTEM || 'MINGW64' } : {}),\r\n },\r\n logPrefix: '[shell]',\r\n signal: context?.signal,\r\n termination: context?.termination,\r\n terminationDeadline: context?.terminationDeadline,\r\n progress: context?.progress,\r\n cleanStderr: (stderr) => stderr\r\n .split('\\n')\r\n .filter(line => !line.includes('process group') && !line.includes('job control'))\r\n .join('\\n')\r\n .trim(),\r\n });\r\n}\r\n\r\nexport function createShellCommandTool(\r\n description: string,\r\n options: ShellCommandToolOptions = {},\r\n): Tool {\r\n const defaultTimeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\r\n // max 是硬上限:default 配置超过 max 时收敛到 max,保证契约自洽\r\n const maxTimeoutMs = Math.max(options.maxTimeoutMs ?? MAX_TIMEOUT_MS, defaultTimeoutMs, 1);\r\n return createTool({\r\n name: 'bash',\r\n description,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n command: { type: 'string' },\r\n timeout: { type: 'number', description: `Optional timeout in milliseconds (max ${maxTimeoutMs}). Defaults to ${defaultTimeoutMs}.` },\r\n },\r\n required: ['command'],\r\n },\r\n render: { call: 'bash', result: 'bash' },\r\n // 计时职责归框架 executor(ticket 023):args.timeout 经 fromArg 消费并 clamp\r\n timeout: {\r\n defaultMs: defaultTimeoutMs,\r\n maxMs: maxTimeoutMs,\r\n fromArg: 'timeout',\r\n },\r\n execute: async (args, context) => {\r\n const { command } = args as { command: string };\r\n // 生效超时(executor clamp 后)经 toolContext 透传(工单 025 进度显示)\r\n const effectiveTimeoutMs = typeof context?.timeoutMs === 'number' ? context.timeoutMs : null;\r\n const result = await runShellCommand(command, options, {\r\n signal: context?.signal,\r\n termination: context?.termination,\r\n terminationDeadline: context?.terminationDeadline,\r\n progress: {\r\n callId: context?.callId,\r\n toolName: 'bash',\r\n timeoutMs: effectiveTimeoutMs,\r\n },\r\n });\r\n return result.output;\r\n },\r\n });\r\n}\r\n","/**\r\n * Shell command quoting utilities.\r\n *\r\n * Ported from Claude Code's shellQuoting.ts + shellQuote.ts.\r\n *\r\n * 核心思路:用单引号整体包裹命令字符串,使所有特殊字符(括号、管道符、\r\n * 分号、$、反引号等)被 bash 视为字面量。然后通过 `eval` 进行二次解析。\r\n *\r\n * 这彻底解决了 `syntax error near unexpected token '('` 问题:\r\n * - 旧方案:command.replace(/\"/g, '\\\\\"') 只转义双引号,\r\n * 导致 `(`, `)`, `;`, `|` 等裸露在 bash 面前\r\n * - 新方案:单引号包裹整个命令,eval 二次解析\r\n */\r\n\r\n// ---------------------------------------------------------------------------\r\n// Heredoc detection\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 检测命令是否包含 heredoc 语法(<<EOF, <<'EOF', <<\"EOF\", <<-EOF 等)。\r\n * 排除位运算左移(<<)的误判。\r\n */\r\nexport function containsHeredoc(command: string): boolean {\r\n // 排除位运算:数字 << 数字、[[ 数字 << 数字 ]]、$(( ... << ... ))\r\n if (\r\n /\\d\\s*<<\\s*\\d/.test(command) ||\r\n /\\[\\[\\s*\\d+\\s*<<\\s*\\d+\\s*\\]\\]/.test(command) ||\r\n /\\$\\(\\(.*<<.*\\)\\)/.test(command)\r\n ) {\r\n return false;\r\n }\r\n\r\n const heredocRegex = /<<-?\\s*(?:(['\"]?)(\\w+)\\1|\\\\(\\w+))/;\r\n return heredocRegex.test(command);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Multiline string detection\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 检测命令中是否包含跨行的引号字符串。\r\n * 这些命令需要特殊引用处理。\r\n */\r\nfunction containsMultilineString(command: string): boolean {\r\n const singleQuoteMultiline = /'(?:[^'\\\\]|\\\\.)*\\n(?:[^'\\\\]|\\\\.)*'/;\r\n const doubleQuoteMultiline = /\"(?:[^\"\\\\]|\\\\.)*\\n(?:[^\"\\\\]|\\\\.)*\"/;\r\n return singleQuoteMultiline.test(command) || doubleQuoteMultiline.test(command);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Stdin redirect\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 检测命令是否已有 stdin 重定向(如 < file, </path, < /dev/null)。\r\n * 排除 << (heredoc)、<< (位运算)、<(进程替换)。\r\n */\r\nexport function hasStdinRedirect(command: string): boolean {\r\n return /(?:^|[\\s;&|])<(?![<(])\\s*\\S+/.test(command);\r\n}\r\n\r\n/**\r\n * 判断是否应该为命令添加 `< /dev/null` stdin 重定向。\r\n *\r\n * - heredoc 命令不需要(它们有自己的输入)\r\n * - 已有 stdin 重定向的命令不需要\r\n */\r\nexport function shouldAddStdinRedirect(command: string): boolean {\r\n if (containsHeredoc(command)) {\r\n return false;\r\n }\r\n if (hasStdinRedirect(command)) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Windows null redirect rewrite\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将 Windows CMD 风格的 `>nul` 重写为 POSIX 的 `>/dev/null`。\r\n *\r\n * Git Bash 看到 `2>nul` 时会创建一个名为 `nul` 的字面量文件——这是\r\n * Windows 保留设备名,极难删除且会破坏 git 操作。\r\n *\r\n * 匹配:`>nul`, `> NUL`, `2>nul`, `&>nul`, `>>nul`(不区分大小写)\r\n * 不匹配:`>null`, `>nullable`, `>nul.txt`\r\n */\r\nconst NUL_REDIRECT_REGEX = /(\\d?&?>+\\s*)[Nn][Uu][Ll](?=\\s|$|[|&;)\\n])/g;\r\n\r\nexport function rewriteWindowsNullRedirect(command: string): string {\r\n return command.replace(NUL_REDIRECT_REGEX, '$1/dev/null');\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Single-quote escaping\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将字符串用 bash 单引号包裹,内部的 `'` 替换为 `'\"'\"'`。\r\n *\r\n * 这是 bash 中在单引号字符串内嵌入单引号的标准技巧:\r\n * - 结束当前单引号\r\n * - 用双引号包裹单引号\r\n * - 重新开始单引号\r\n *\r\n * @example\r\n * escapeForSingleQuote(\"it's\") → \"it'\\\"'\\\"'s\"\r\n * wrapSingleQuote(\"echo hello\") → \"'echo hello'\"\r\n * wrapSingleQuote(\"echo it's\") → \"'echo it'\\\"'\\\"'s'\"\r\n */\r\nfunction escapeForSingleQuote(str: string): string {\r\n return str.replace(/'/g, `'\\\"'\\\"'`);\r\n}\r\n\r\nfunction wrapSingleQuote(str: string): string {\r\n return `'${escapeForSingleQuote(str)}'`;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Main quoting function\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 对 shell 命令进行安全引用,保留 heredoc 和多行字符串的完整性。\r\n *\r\n * 返回的字符串可直接用于 `eval`,例如:\r\n * const quoted = quoteShellCommand('echo \"hello (world)\"');\r\n * // quoted === \"'echo \\\"hello (world)\\\"' < /dev/null\"\r\n * // 完整命令: eval 'echo \"hello (world)\"' < /dev/null\r\n *\r\n * @param command 要引用的原始命令\r\n * @param addStdinRedirect 是否追加 `< /dev/null` stdin 重定向\r\n * @returns 安全引用后的命令字符串\r\n */\r\nexport function quoteShellCommand(\r\n command: string,\r\n addStdinRedirect: boolean = true,\r\n): string {\r\n // heredoc 和多行字符串需要特殊处理\r\n // shell-quote 库会错误地转义这些内容中的 ! 为 \\!\r\n if (containsHeredoc(command) || containsMultilineString(command)) {\r\n const quoted = wrapSingleQuote(command);\r\n\r\n // heredoc 自带输入,不加 stdin redirect\r\n if (containsHeredoc(command)) {\r\n return quoted;\r\n }\r\n\r\n return addStdinRedirect ? `${quoted} < /dev/null` : quoted;\r\n }\r\n\r\n // 普通命令:单引号包裹\r\n const quoted = wrapSingleQuote(command);\r\n\r\n if (addStdinRedirect) {\r\n return `${quoted} < /dev/null`;\r\n }\r\n\r\n return quoted;\r\n}\r\n","/**\r\n * Shell 共享核心(ticket 024 / ADR-0005)\r\n *\r\n * bash 与 powershell 共用的「spawn + collect + 截断落盘 + 终止收集」管线:\r\n * - 正常完成:沿用既有语义(成功 stdout/stderr 独立截断落盘;失败合并截断后 reject)。\r\n * - 终止收集(toolContext.signal aborted,ticket 023 合并 signal 契约):\r\n * kill(Windows taskkill /PID <pid> /T /F 进程树;POSIX 进程组 SIGKILL)\r\n * → 继续读 stdout/stderr 到 EOF(上限 1s,孙进程占 pipe 兜底)\r\n * → resolve(不再 reject),结果文本为部分输出 + 尾部 <shell_metadata> 块。\r\n *\r\n * 计时职责归框架 executor(Tool.timeout 声明契约):本模块不设内部 setTimeout\r\n * race;args.timeout 由 executor 经 fromArg 消费并 clamp。\r\n */\r\n\r\nimport { spawn } from 'child_process';\r\nimport { mkdir, writeFile } from 'fs/promises';\r\nimport * as path from 'path';\r\nimport type { ToolTerminationReason } from '@agentdevjs/core';\r\nimport { emitNotification, createToolProgress } from '@agentdevjs/core';\r\n\r\n/** drain 上限:kill 后等待管道 EOF 的时间(孙进程占 pipe 兜底)。 */\r\nconst TERMINATION_DRAIN_FALLBACK_MS = 1000;\r\n/** Reserve a small margin so the tool promise settles before executor's deadline. */\r\nconst TERMINATION_DRAIN_SAFETY_MS = 50;\r\n\r\n/** 进度发射节流(工单 025):notification 层另有 100ms 节流兜底。 */\r\nconst PROGRESS_EMIT_INTERVAL_MS = 300;\r\n\r\n/** 进度 outputTail 行数上限。 */\r\nconst PROGRESS_TAIL_LINES = 5;\r\n\r\n/** 元数据块标记(仅终止态出现在结果尾部)。 */\r\nexport const SHELL_METADATA_OPEN = '<shell_metadata>';\r\nexport const SHELL_METADATA_CLOSE = '</shell_metadata>';\r\n\r\nexport interface ShellRunResult {\r\n stdout: string;\r\n stderr: string;\r\n output: string;\r\n}\r\n\r\nexport interface ShellRunContext {\r\n /** 框架注入的合并 signal(用户打断与框架超时共用,见 ticket 023) */\r\n signal?: AbortSignal;\r\n /** 终止原因查询(executor 注入;未走终止协议时恒返回 null) */\r\n termination?: () => ToolTerminationReason | null;\r\n /** 当前终止 settle 的绝对截止时间(epoch ms);shell drain 消费同一预算 */\r\n terminationDeadline?: () => number | null;\r\n /**\r\n * 进度发射上下文(工单 025):提供后执行中每 ~300ms 发射一次\r\n * tool.progress 通知;缺省不发射(纯增量,行为不变)。\r\n */\r\n progress?: {\r\n /** LLM 生成的 call.id */\r\n callId?: string;\r\n /** 工具名称 */\r\n toolName: string;\r\n /** 本次调用生效超时(毫秒);未声明 timeout 时为 null */\r\n timeoutMs: number | null;\r\n };\r\n}\r\n\r\n/** 取输出尾部 N 行(进度预览)。 */\r\nfunction tailLines(text: string, lines: number): string {\r\n if (!text) return '';\r\n const parts = text.split('\\n');\r\n const tail = parts.slice(-lines).join('\\n');\r\n return tail.length > 500 ? tail.slice(-500) : tail;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 输出截断 + 落盘持久化(自 tools.ts 迁入共享;bash-output-* 落盘机制不变)\r\n// ---------------------------------------------------------------------------\r\n\r\nconst MAX_OUTPUT_LENGTH = 30_000;\r\n\r\nfunction timestampSlug(): string {\r\n const now = new Date();\r\n const ts = now.getFullYear().toString() +\r\n String(now.getMonth() + 1).padStart(2, '0') +\r\n String(now.getDate()).padStart(2, '0') + '-' +\r\n String(now.getHours()).padStart(2, '0') +\r\n String(now.getMinutes()).padStart(2, '0') +\r\n String(now.getSeconds()).padStart(2, '0');\r\n const suffix = Math.random().toString(36).slice(2, 8);\r\n return `${ts}-${suffix}`;\r\n}\r\n\r\n/**\r\n * 截断输出并持久化完整内容到磁盘。\r\n *\r\n * 当输出超过 limit 时:\r\n * 1. 将完整输出写入 workdir/.agentdev/temp/<filePrefix>-<timestamp>-<random>.log\r\n * (filePrefix 缺省 bash-output;非 bash 调用方传自己的工具名,便于溯源)\r\n * 2. 返回截断版本(头 60% + 尾 40%),中间插入截断提示和文件路径引用\r\n *\r\n * 如果写盘失败,fallback 到纯截断(不丢失截断提示,但完整内容不可恢复)。\r\n *\r\n * @param forcePersist true 时无论长度一律落盘(终止态使用:被杀进程的已积累\r\n * 输出必须可恢复),并返回 logPath 供元数据引用。\r\n * @param filePrefix 落盘文件名前缀(缺省 'bash-output',bash 工具行为不变)\r\n */\r\nexport async function processOutputWithPersistence(\r\n output: string,\r\n workdir: string,\r\n limit: number = MAX_OUTPUT_LENGTH,\r\n forcePersist: boolean = false,\r\n filePrefix: string = 'bash-output',\r\n): Promise<[string, string | null]> {\r\n if (!forcePersist && output.length <= limit) return [output, null];\r\n\r\n // 尝试将完整输出持久化到磁盘\r\n let filePath: string | null = null;\r\n try {\r\n const tempDir = path.join(workdir, '.agentdev', 'temp');\r\n const fileName = `${filePrefix}-${timestampSlug()}.log`;\r\n filePath = path.join(tempDir, fileName);\r\n\r\n await mkdir(tempDir, { recursive: true });\r\n await writeFile(filePath, output, 'utf-8');\r\n } catch (err) {\r\n console.error(`[shell] Failed to persist output: ${err}`);\r\n filePath = null;\r\n }\r\n\r\n if (output.length <= limit) return [output, filePath];\r\n\r\n const headSize = Math.floor(limit * 0.6);\r\n const tailSize = limit - headSize;\r\n const head = output.slice(0, headSize);\r\n const tail = output.slice(-tailSize);\r\n const omitted = output.length - limit;\r\n const totalKB = Math.round(output.length / 1024);\r\n\r\n const persistNotice = filePath\r\n ? `[Full output (${totalKB}KB) saved to: ${filePath}]\\nUse the read tool to access the full output if needed.\\n`\r\n : '';\r\n\r\n return [\r\n head +\r\n `\\n\\n... [truncated: omitted ${omitted} characters (${totalKB}KB total)] ...\\n${persistNotice}\\n` +\r\n tail,\r\n filePath,\r\n ];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// kill 工具(Windows taskkill 进程树 / POSIX 进程组)\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction makeKillChild(child: import('child_process').ChildProcess): () => void {\r\n return () => {\r\n try {\r\n if (process.platform === 'win32') {\r\n const killer = spawn('taskkill', ['/PID', String(child.pid), '/T', '/F'], { stdio: 'ignore' });\r\n killer.once('error', (error) => {\r\n console.warn(`[shell] taskkill failed for PID=${child.pid}:`, error);\r\n child.kill('SIGKILL');\r\n });\r\n killer.once('close', (code) => {\r\n if (code !== 0) {\r\n console.warn(`[shell] taskkill exited with code ${code} for PID=${child.pid}; killing the direct child`);\r\n child.kill('SIGKILL');\r\n }\r\n });\r\n } else {\r\n process.kill(-child.pid!, 'SIGKILL');\r\n }\r\n } catch {\r\n // 进程可能已经退出\r\n }\r\n };\r\n}\r\n\r\n/** kill 后等待管道 EOF:正常在 close 触发,孙进程继承 pipe 句柄时以超时兜底。 */\r\nfunction drainToEof(child: import('child_process').ChildProcess, timeoutMs: number): Promise<void> {\r\n return new Promise((resolve) => {\r\n let done = false;\r\n const finish = () => {\r\n if (done) return;\r\n done = true;\r\n clearTimeout(timer);\r\n resolve();\r\n };\r\n const timer = setTimeout(finish, timeoutMs);\r\n timer.unref();\r\n child.once('close', finish);\r\n });\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// <shell_metadata> 结果块(仅终止态出现,字段见工单 024 步骤 4)\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface ShellMetadataFields {\r\n terminated: boolean;\r\n reason: ToolTerminationReason;\r\n durationMs: number;\r\n exitCode: number | null;\r\n outputBytes: number;\r\n truncated: boolean;\r\n logPath: string | null;\r\n}\r\n\r\nexport function formatShellMetadata(meta: ShellMetadataFields): string {\r\n return [\r\n SHELL_METADATA_OPEN,\r\n `terminated: ${meta.terminated}`,\r\n `reason: ${meta.reason}`,\r\n `durationMs: ${Math.round(meta.durationMs)}`,\r\n `exitCode: ${meta.exitCode === null ? 'null' : meta.exitCode}`,\r\n `outputBytes: ${meta.outputBytes}`,\r\n `truncated: ${meta.truncated}`,\r\n `logPath: ${meta.logPath ?? 'null'}`,\r\n SHELL_METADATA_CLOSE,\r\n ].join('\\n');\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 共享运行核心\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface SharedRunOptions extends ShellRunContext {\r\n workdir: string;\r\n execPath: string;\r\n args: string[];\r\n env?: NodeJS.ProcessEnv;\r\n /** 日志前缀('[shell]' / '[powershell]') */\r\n logPrefix: string;\r\n /** stderr 清理(bash 过滤 job control 噪音;powershell 仅 trim),在截断/合并前调用 */\r\n cleanStderr?: (stderr: string) => string;\r\n}\r\n\r\n/**\r\n * spawn + collect + 截断落盘 + 终止收集。\r\n *\r\n * - signal 未触发:行为与历史实现一致(成功 resolve 截断输出;失败 reject 截断信息)。\r\n * - signal aborted:kill → drain 到 EOF(≤1s)→ resolve 部分输出 + 尾部元数据块,\r\n * 已积累输出无条件落盘(logPath 入元数据)。\r\n */\r\nexport function runCollectedProcess(\r\n opts: SharedRunOptions,\r\n): Promise<ShellRunResult> {\r\n const { workdir, logPrefix, signal, termination, progress } = opts;\r\n\r\n const startedAt = Date.now();\r\n const cleanForRun = (raw: string): string =>\r\n opts.cleanStderr ? opts.cleanStderr(raw) : raw.trim();\r\n const readTermination = (): ToolTerminationReason => termination?.() ?? 'user';\r\n\r\n return new Promise<ShellRunResult>((resolve, reject) => {\r\n let stdout = '';\r\n let stderr = '';\r\n let settled = false;\r\n\r\n // 进度发射(工单 025):~300ms 节流,发射失败静默忽略(纯呈现通道)\r\n let lastProgressAt = 0;\r\n let progressTimer: ReturnType<typeof setInterval> | null = null;\r\n const stopProgressTimer = () => {\r\n if (progressTimer !== null) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n }\r\n };\r\n const emitProgress = () => {\r\n if (!progress) return;\r\n const now = Date.now();\r\n if (now - lastProgressAt < PROGRESS_EMIT_INTERVAL_MS) return;\r\n lastProgressAt = now;\r\n try {\r\n emitNotification(createToolProgress({\r\n callId: progress.callId ?? '',\r\n toolName: progress.toolName,\r\n startedAt,\r\n elapsedMs: now - startedAt,\r\n timeoutMs: progress.timeoutMs,\r\n outputTail: tailLines(stdout || stderr, PROGRESS_TAIL_LINES),\r\n }));\r\n } catch {\r\n // 进度是纯增量呈现通道,任何失败不影响执行主流程\r\n }\r\n };\r\n\r\n /** settle 收尾统一出口:停进度计时器 */\r\n const finishSettled = (fn: () => void) => {\r\n stopProgressTimer();\r\n fn();\r\n };\r\n\r\n /** 终止态收尾:已积累输出无条件落盘 → 组装「部分输出 + 元数据块」结果。 */\r\n const finishTerminated = async (): Promise<ShellRunResult> => {\r\n const combined = [stdout, cleanForRun(stderr)].filter(Boolean).join('\\n');\r\n const [text, logPath] = await processOutputWithPersistence(\r\n combined || '',\r\n workdir,\r\n MAX_OUTPUT_LENGTH,\r\n true,\r\n );\r\n const meta: ShellMetadataFields = {\r\n terminated: true,\r\n reason: readTermination(),\r\n durationMs: Date.now() - startedAt,\r\n exitCode: null,\r\n outputBytes: Buffer.byteLength(combined, 'utf-8'),\r\n truncated: text.length < combined.length,\r\n logPath,\r\n };\r\n return {\r\n stdout: text,\r\n stderr: '',\r\n output: text ? `${text}\\n${formatShellMetadata(meta)}` : formatShellMetadata(meta),\r\n };\r\n };\r\n\r\n const child = spawn(opts.execPath, opts.args, {\r\n cwd: workdir,\r\n env: opts.env ?? { ...process.env },\r\n stdio: ['ignore', 'pipe', 'pipe'],\r\n windowsHide: true,\r\n // On Linux/macOS, detached: true puts the child in its own process group\r\n // so that process.kill(-pid) can terminate the entire group on abort.\r\n ...(process.platform !== 'win32' ? { detached: true } : {}),\r\n });\r\n\r\n const killChild = makeKillChild(child);\r\n\r\n // 执行开始:启动进度周期发射\r\n if (progress) {\r\n emitProgress();\r\n progressTimer = setInterval(emitProgress, PROGRESS_EMIT_INTERVAL_MS);\r\n progressTimer.unref?.();\r\n }\r\n\r\n const cleanupSignal = () => {\r\n signal?.removeEventListener('abort', onAbort);\r\n };\r\n\r\n /**\r\n * 终止收集(ADR-0005 中断即结果):kill → drain 到 EOF → resolve。\r\n * 被杀进程拿不到 exit code,元数据中记 null。\r\n */\r\n const terminateAndCollect = () => {\r\n if (settled) return;\r\n settled = true;\r\n cleanupSignal();\r\n stopProgressTimer();\r\n console.log(`${logPrefix} signal abort detected, killing child PID=${child.pid}`);\r\n killChild();\r\n const deadline = opts.terminationDeadline?.();\r\n const drainMs = Math.max(\r\n 0,\r\n (deadline ?? (Date.now() + TERMINATION_DRAIN_FALLBACK_MS)) - Date.now() - TERMINATION_DRAIN_SAFETY_MS,\r\n );\r\n void drainToEof(child, drainMs)\r\n .then(finishTerminated)\r\n .then((value) => finishSettled(() => resolve(value)))\r\n .catch((err) => finishSettled(() => reject(err)));\r\n };\r\n\r\n const onAbort = () => terminateAndCollect();\r\n\r\n if (signal) {\r\n if (signal.aborted) {\r\n // 执行前已中断:不执行命令,直接以终止态收尾\r\n terminateAndCollect();\r\n return;\r\n }\r\n signal.addEventListener('abort', onAbort, { once: true });\r\n }\r\n\r\n child.stdout?.on('data', (data) => {\r\n stdout += data.toString();\r\n });\r\n\r\n child.stderr?.on('data', (data) => {\r\n stderr += data.toString();\r\n });\r\n\r\n child.on('close', (code) => {\r\n if (settled) return;\r\n settled = true;\r\n cleanupSignal();\r\n stopProgressTimer();\r\n\r\n const cleanStderr = cleanForRun(stderr);\r\n\r\n if (signal?.aborted) {\r\n // close 在终止收集之后到达(罕见竞态):仍以终止态收尾\r\n void finishTerminated()\r\n .then((value) => finishSettled(() => resolve(value)))\r\n .catch((err) => finishSettled(() => reject(err)));\r\n return;\r\n }\r\n\r\n if (code === 0) {\r\n // 成功:stdout 和 stderr 分别独立截断(现状语义,无元数据块)\r\n Promise.all([\r\n processOutputWithPersistence(stdout || '', workdir),\r\n processOutputWithPersistence(cleanStderr || '', workdir),\r\n ]).then(([truncatedStdout, truncatedStderr]) => {\r\n finishSettled(() => resolve({\r\n stdout: truncatedStdout[0],\r\n stderr: truncatedStderr[0],\r\n output: truncatedStdout[0] || truncatedStderr[0],\r\n }));\r\n }).catch(err => {\r\n finishSettled(() => reject(err));\r\n });\r\n } else {\r\n // 失败:合并 stdout + stderr 后统一截断(现状语义)\r\n const combined = [stdout, cleanStderr].filter(Boolean).join('\\n\\n--- stderr ---\\n');\r\n processOutputWithPersistence(combined, workdir).then(([truncated]) => {\r\n finishSettled(() => reject(new Error(truncated || `Command failed with exit code ${code}`)));\r\n }).catch(err => {\r\n finishSettled(() => reject(err));\r\n });\r\n }\r\n });\r\n\r\n child.on('error', (err) => {\r\n if (settled) return;\r\n settled = true;\r\n cleanupSignal();\r\n finishSettled(() => reject(err));\r\n });\r\n });\r\n}\r\n","/**\r\n * PowerShell 命令执行工具\r\n *\r\n * 提供 PowerShell 路径检测、命令执行和工具定义。\r\n * 与 bash 工具平行,共用 shell-core.ts 的「spawn + collect + 截断落盘 +\r\n * 终止收集」管线(ticket 024),行为逐项一致(render 模板本就共用 bash.render.ts)。\r\n */\r\n\r\nimport { execSync } from 'child_process';\r\nimport { existsSync } from 'fs';\r\nimport type { Tool } from '@agentdevjs/core';\r\nimport { createTool } from '@agentdevjs/core';\r\nimport {\r\n runCollectedProcess,\r\n type ShellRunContext,\r\n} from './shell-core.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// PowerShell 路径检测\r\n// ---------------------------------------------------------------------------\r\n\r\nlet cachedPsPath: string | null | undefined = undefined;\r\n\r\n/**\r\n * 查找 PowerShell 可执行文件路径。\r\n *\r\n * 查找顺序:\r\n * 1. configuredPath 参数(来自 manifest 配置)\r\n * 2. 环境变量 AGENTDEV_POWERSHELL_PATH\r\n * 3. pwsh(PowerShell 7+,跨平台)\r\n * 4. powershell.exe(Windows PowerShell 5.1)\r\n * 5. Windows 已知系统路径\r\n *\r\n * 返回 null 表示未找到。\r\n */\r\nexport function findPowerShellPath(configuredPath?: string): string | null {\r\n if (cachedPsPath !== undefined) return cachedPsPath;\r\n\r\n // 0. 用户在 manifest 中配置的路径\r\n if (configuredPath && existsSync(configuredPath)) {\r\n cachedPsPath = configuredPath;\r\n return cachedPsPath;\r\n }\r\n\r\n // 1. 环境变量\r\n if (process.env.AGENTDEV_POWERSHELL_PATH && existsSync(process.env.AGENTDEV_POWERSHELL_PATH)) {\r\n cachedPsPath = process.env.AGENTDEV_POWERSHELL_PATH;\r\n return cachedPsPath;\r\n }\r\n\r\n const isWin = process.platform === 'win32';\r\n const whereCmd = isWin ? 'where' : 'which';\r\n\r\n // 2. pwsh (PowerShell Core 7+)\r\n try {\r\n const result = execSync(`${whereCmd} pwsh`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });\r\n const p = result.split('\\n').map(l => l.trim()).filter(Boolean)[0];\r\n if (p && existsSync(p)) {\r\n cachedPsPath = p;\r\n return cachedPsPath;\r\n }\r\n } catch { /* pwsh not installed */ }\r\n\r\n // 3. powershell (Windows PowerShell 5.1, Windows only)\r\n if (isWin) {\r\n try {\r\n const result = execSync(`${whereCmd} powershell`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });\r\n const p = result.split('\\n').map(l => l.trim()).filter(Boolean)[0];\r\n if (p && existsSync(p)) {\r\n cachedPsPath = p;\r\n return cachedPsPath;\r\n }\r\n } catch { /* not in PATH */ }\r\n\r\n // 4. Windows 系统默认路径\r\n const sysPath = 'C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe';\r\n if (existsSync(sysPath)) {\r\n cachedPsPath = sysPath;\r\n return cachedPsPath;\r\n }\r\n }\r\n\r\n cachedPsPath = null;\r\n return null;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 工具定义与超时契约\r\n// ---------------------------------------------------------------------------\r\n\r\nconst DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes\r\nconst MAX_TIMEOUT_MS = 600_000; // 10 minutes\r\n\r\nexport interface PowerShellToolOptions {\r\n workspaceDir?: string;\r\n workdir?: string;\r\n resourceRoot?: string;\r\n /** 已检测到的 PowerShell 路径 */\r\n psPath?: string;\r\n /** 覆盖默认超时(manifest 配置 defaultTimeoutMs);缺省 120000 */\r\n timeoutMs?: number;\r\n /** 覆盖超时上限(manifest 配置 maxTimeoutMs);缺省 600000 */\r\n maxTimeoutMs?: number;\r\n}\r\n\r\n/**\r\n * 运行 PowerShell 命令(支持 AbortSignal 中断;终止时收集部分输出并附元数据块)\r\n */\r\nexport async function runPowerShellCommand(\r\n command: string,\r\n options: PowerShellToolOptions = {},\r\n context?: ShellRunContext,\r\n): Promise<{ stdout: string; stderr: string; output: string }> {\r\n const workspaceDir = options.workspaceDir || process.cwd();\r\n const workdir = options.workdir || workspaceDir;\r\n const psPath = options.psPath || findPowerShellPath();\r\n\r\n if (!psPath) {\r\n throw new Error('PowerShell not found.');\r\n }\r\n\r\n console.log(`[powershell] ${command}`);\r\n\r\n return runCollectedProcess({\r\n workdir,\r\n execPath: psPath,\r\n args: ['-NoProfile', '-NonInteractive', '-Command', command],\r\n env: { ...process.env },\r\n logPrefix: '[powershell]',\r\n signal: context?.signal,\r\n termination: context?.termination,\r\n terminationDeadline: context?.terminationDeadline,\r\n progress: context?.progress,\r\n });\r\n}\r\n\r\nexport function createPowerShellTool(\r\n description: string,\r\n options: PowerShellToolOptions = {},\r\n): Tool {\r\n const defaultTimeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\r\n // max 是硬上限:default 配置超过 max 时收敛到 max,保证契约自洽\r\n const maxTimeoutMs = Math.max(options.maxTimeoutMs ?? MAX_TIMEOUT_MS, defaultTimeoutMs, 1);\r\n return createTool({\r\n name: 'powershell',\r\n description,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n command: { type: 'string' },\r\n timeout: { type: 'number', description: `Optional timeout in milliseconds (max ${maxTimeoutMs}). Defaults to ${defaultTimeoutMs}.` },\r\n },\r\n required: ['command'],\r\n },\r\n render: { call: 'bash', result: 'bash' },\r\n // 与 bash 一致:计时职责归框架 executor(ticket 023)\r\n timeout: {\r\n defaultMs: defaultTimeoutMs,\r\n maxMs: maxTimeoutMs,\r\n fromArg: 'timeout',\r\n },\r\n execute: async (args, context) => {\r\n const { command } = args as { command: string };\r\n // 生效超时(executor clamp 后)经 toolContext 透传(工单 025 进度显示)\r\n const effectiveTimeoutMs = typeof context?.timeoutMs === 'number' ? context.timeoutMs : null;\r\n const result = await runPowerShellCommand(command, options, {\r\n signal: context?.signal,\r\n termination: context?.termination,\r\n terminationDeadline: context?.terminationDeadline,\r\n progress: {\r\n callId: context?.callId,\r\n toolName: 'powershell',\r\n timeoutMs: effectiveTimeoutMs,\r\n },\r\n });\r\n return result.output;\r\n },\r\n });\r\n}\r\n","/**\r\n * Safe Trash 工具定义\r\n */\r\n\r\nimport { join } from 'path';\r\nimport { createTool } from '@agentdevjs/core';\r\nimport type { Tool } from '@agentdevjs/core';\r\nimport { safeRm, listTrashed, restore } from './lib/index.js';\r\n\r\nfunction formatSize(bytes: number): string {\r\n if (bytes === 0) return '0 B';\r\n const k = 1024;\r\n const sizes = ['B', 'KB', 'MB', 'GB'];\r\n const i = Math.floor(Math.log(bytes) / Math.log(k));\r\n return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;\r\n}\r\n\r\nexport function createSafeTrashDeleteTool(workspaceDir: string = process.cwd()): Tool {\r\n const defaultTrashDir = join(workspaceDir, '.trash');\r\n return createTool({\r\n name: 'safe_trash_delete',\r\n description: '安全删除文件或目录,移动到垃圾目录而非永久删除。',\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n paths: { type: 'array', items: { type: 'string' } },\r\n trashDir: { type: 'string' },\r\n },\r\n required: ['paths'],\r\n },\r\n render: { call: 'trash-delete', result: 'trash-delete' },\r\n execute: async (args) => {\r\n const { paths, trashDir } = args as { paths: string[]; trashDir?: string };\r\n const trashDirPath = trashDir || defaultTrashDir;\r\n const result = safeRm(workspaceDir, paths.join(' '), trashDirPath, null, 0);\r\n return {\r\n success: result.success,\r\n moved_count: result.movedCount,\r\n moved: result.moved,\r\n failed: result.failed,\r\n };\r\n },\r\n });\r\n}\r\n\r\nexport function createSafeTrashListTool(workspaceDir: string = process.cwd()): Tool {\r\n const defaultTrashDir = join(workspaceDir, '.trash');\r\n return createTool({\r\n name: 'safe_trash_list',\r\n description: '列出垃圾目录中的所有可恢复文件。',\r\n parallelizable: true,\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n trashDir: { type: 'string' },\r\n },\r\n },\r\n render: { call: 'trash-list', result: 'trash-list' },\r\n execute: async (args) => {\r\n const { trashDir } = args as { trashDir?: string };\r\n const trashDirPath = trashDir || defaultTrashDir;\r\n const result = listTrashed(trashDirPath, null);\r\n return {\r\n success: result.success,\r\n total: result.total,\r\n files: result.files.map((f) => ({ ...f, size_formatted: formatSize(f.size || 0) })),\r\n };\r\n },\r\n });\r\n}\r\n\r\nexport function createSafeTrashRestoreTool(workspaceDir: string = process.cwd()): Tool {\r\n const defaultTrashDir = join(workspaceDir, '.trash');\r\n return createTool({\r\n name: 'safe_trash_restore',\r\n description: '从垃圾目录恢复文件到原位置。',\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n target: {\r\n oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'array', items: { oneOf: [{ type: 'number' }, { type: 'string' }] } }],\r\n },\r\n trashDir: { type: 'string' },\r\n overwrite: { type: 'boolean' },\r\n },\r\n required: ['target'],\r\n },\r\n render: { call: 'trash-restore', result: 'trash-restore' },\r\n execute: async (args) => {\r\n const { target, trashDir, overwrite } = args as { target: number | string | number[] | string[]; trashDir?: string; overwrite?: boolean };\r\n const trashDirPath = trashDir || defaultTrashDir;\r\n const result = restore(trashDirPath, target, null, overwrite || false, false, true);\r\n return {\r\n success: result.success,\r\n restored_count: result.restoredCount,\r\n restored: result.restored,\r\n failed: result.failed,\r\n };\r\n },\r\n });\r\n}\r\n\r\nexport const safeTrashDeleteTool = createSafeTrashDeleteTool();\r\nexport const safeTrashListTool = createSafeTrashListTool();\r\nexport const safeTrashRestoreTool = createSafeTrashRestoreTool();\r\n","/**\r\n * Safe Trash 错误处理\r\n */\r\n\r\nimport { ErrorCode } from './types.js';\r\n\r\n/**\r\n * 自定义错误类\r\n */\r\nexport class SafeRmError extends Error {\r\n readonly code: ErrorCode;\r\n readonly details: Record<string, unknown>;\r\n\r\n constructor(\r\n code: ErrorCode,\r\n message: string,\r\n details?: Record<string, unknown>\r\n ) {\r\n super(message);\r\n this.name = 'SafeRmError';\r\n this.code = code;\r\n this.details = details || {};\r\n }\r\n\r\n toDict(): Record<string, unknown> {\r\n return {\r\n success: false,\r\n error_code: this.code,\r\n error_message: this.message,\r\n error_details: this.details,\r\n };\r\n }\r\n\r\n toSafeRmResult(): import('./types.js').SafeRmResult {\r\n return {\r\n success: false,\r\n moved: [],\r\n failed: [this.message],\r\n movedCount: 0,\r\n failedCount: 1,\r\n };\r\n }\r\n}\r\n\r\nexport function throwInvalidPathError(path: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_PATH, '路径无效', { path });\r\n}\r\n\r\nexport function throwPathTooLongError(path: string, length: number, max: number): never {\r\n throw new SafeRmError(ErrorCode.ERROR_PATH_TOO_LONG, `路径过长(最大 ${max} 字符)`, { path, length, max_length: max });\r\n}\r\n\r\nexport function throwIllegalCharError(path: string, char: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_PATH, `路径包含非法字符: '${char}'`, { path, illegal_char: char });\r\n}\r\n\r\nexport function throwFileNotFoundError(path: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_FILE_NOT_FOUND, '文件不存在', { path });\r\n}\r\n\r\nexport function throwPermissionDeniedError(path: string, action: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_PERMISSION_DENIED, `权限不足,无法${action}`, { path });\r\n}\r\n\r\nexport function throwSystemFileError(path: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_SYSTEM_FILE, '系统文件受保护,无法删除', { path });\r\n}\r\n\r\nexport function throwFileExistsError(path: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_FILE_EXISTS, '文件已存在', { path });\r\n}\r\n\r\nexport function throwNoTrashDirError(trashDir: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_NO_TRASH_DIR, '垃圾目录不存在', { trash_dir: trashDir });\r\n}\r\n\r\nexport function throwInvalidOperatorError(operator: string): never {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, '操作者名称无效', { operator });\r\n}\r\n\r\nexport function throwNoFilesSpecifiedError(): never {\r\n throw new SafeRmError(ErrorCode.ERROR_NO_FILES_SPECIFIED, '请指定要删除的文件', {});\r\n}\r\n","/**\r\n * 文件系统工具类\r\n */\r\n\r\nimport {\r\n existsSync,\r\n lstatSync,\r\n statSync,\r\n readdirSync,\r\n mkdirSync,\r\n renameSync,\r\n rmSync,\r\n readFileSync,\r\n writeFileSync,\r\n copyFileSync,\r\n} from 'fs';\r\nimport { join, dirname, basename, normalize } from 'path';\r\nimport {\r\n SafeRmError,\r\n throwPathTooLongError,\r\n throwIllegalCharError,\r\n throwPermissionDeniedError,\r\n} from './errors.js';\r\nimport { ErrorCode } from './types.js';\r\n\r\nconst MAX_PATH = 260;\r\nconst ILLEGAL_CHARS = '<>:\"|?*';\r\n\r\nconst SYSTEM_DIRS = [\r\n process.env.SystemRoot || 'C:\\\\Windows',\r\n 'C:\\\\Windows',\r\n 'C:\\\\Program Files',\r\n 'C:\\\\Program Files (x86)',\r\n 'C:\\\\ProgramData',\r\n];\r\n\r\nexport class FileSystem {\r\n static validatePath(path: string): void {\r\n if (!path || typeof path !== 'string') {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_PATH, '路径无效', { path: String(path) });\r\n }\r\n if (path.length > MAX_PATH) {\r\n throwPathTooLongError(path, path.length, MAX_PATH);\r\n }\r\n for (const char of ILLEGAL_CHARS) {\r\n if (path.includes(char)) {\r\n const colonContext = path.length >= 2 && path[1] === ':' && char === ':';\r\n if (!colonContext) {\r\n throwIllegalCharError(path, char);\r\n }\r\n }\r\n }\r\n }\r\n\r\n static exists(path: string): boolean {\r\n return existsSync(path);\r\n }\r\n\r\n static lexists(path: string): boolean {\r\n try {\r\n lstatSync(path);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n static isdir(path: string): boolean {\r\n try {\r\n return statSync(path).isDirectory();\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n static isfile(path: string): boolean {\r\n try {\r\n return statSync(path).isFile();\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n static islink(path: string): boolean {\r\n try {\r\n return lstatSync(path).isSymbolicLink();\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n static getsize(path: string): number {\r\n try {\r\n return statSync(path).size;\r\n } catch {\r\n return 0;\r\n }\r\n }\r\n\r\n static makedirs(path: string, mode: number = 0o755): void {\r\n if (existsSync(path) && FileSystem.isdir(path)) return;\r\n try {\r\n mkdirSync(path, { mode, recursive: true });\r\n } catch (error: any) {\r\n if (error.code === 'EACCES' || error.code === 'EPERM') {\r\n throwPermissionDeniedError(path, '创建目录');\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_CREATE_PARENT_FAILED, `无法创建目录: ${error.message}`, { path, os_error: error.code });\r\n }\r\n }\r\n\r\n static move(src: string, dst: string): void {\r\n try {\r\n const dstDir = dirname(dst);\r\n if (!existsSync(dstDir)) {\r\n this.makedirs(dstDir);\r\n }\r\n renameSync(src, dst);\r\n } catch (error: any) {\r\n if (error.code === 'EXDEV' || error.code === 'ENOENT') {\r\n try {\r\n this._copyAndDelete(src, dst);\r\n } catch (copyError: any) {\r\n if (copyError.code === 'EACCES' || copyError.code === 'EPERM') {\r\n throw new SafeRmError(ErrorCode.ERROR_FILE_LOCKED, '文件被占用或无权限访问', { source: src, destination: dst });\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `跨卷移动文件失败: ${copyError.message}`, { source: src, destination: dst });\r\n }\r\n return;\r\n }\r\n if (error.code === 'EACCES' || error.code === 'EPERM') {\r\n throw new SafeRmError(ErrorCode.ERROR_FILE_LOCKED, '文件被占用或无权限访问', { source: src, destination: dst });\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `移动文件失败: ${error.message}`, { source: src, destination: dst });\r\n }\r\n }\r\n\r\n private static _copyAndDelete(src: string, dst: string): void {\r\n const srcStat = statSync(src);\r\n if (srcStat.isDirectory()) {\r\n if (!existsSync(dst)) {\r\n mkdirSync(dst, { recursive: true });\r\n }\r\n const entries = readdirSync(src, { withFileTypes: true });\r\n for (const entry of entries) {\r\n const srcPath = join(src, entry.name);\r\n const dstPath = join(dst, entry.name);\r\n this._copyAndDelete(srcPath, dstPath);\r\n }\r\n rmSync(src, { recursive: true, force: true });\r\n } else {\r\n copyFileSync(src, dst);\r\n rmSync(src, { force: true });\r\n }\r\n }\r\n\r\n static remove(path: string): void {\r\n try {\r\n if (FileSystem.isdir(path)) {\r\n rmSync(path, { recursive: true, force: true });\r\n } else {\r\n rmSync(path, { force: true });\r\n }\r\n } catch (error: any) {\r\n if (error.code === 'EACCES' || error.code === 'EPERM') {\r\n throwPermissionDeniedError(path, '删除');\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `删除失败: ${error.message}`, { path });\r\n }\r\n }\r\n\r\n static isSystemFile(path: string): boolean {\r\n try {\r\n const absPath = normalize(path).toLowerCase();\r\n for (const sysDir of SYSTEM_DIRS) {\r\n if (sysDir && absPath.startsWith(normalize(sysDir).toLowerCase())) {\r\n return true;\r\n }\r\n }\r\n } catch {}\r\n return false;\r\n }\r\n\r\n static readFile(path: string): string {\r\n try {\r\n return readFileSync(path, 'utf-8');\r\n } catch (error: any) {\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `读取文件失败: ${error.message}`, { path });\r\n }\r\n }\r\n\r\n static writeFile(path: string, content: string): void {\r\n try {\r\n const dir = dirname(path);\r\n if (!existsSync(dir)) {\r\n this.makedirs(dir);\r\n }\r\n writeFileSync(path, content, 'utf-8');\r\n } catch (error: any) {\r\n if (error.code === 'EACCES' || error.code === 'EPERM') {\r\n throwPermissionDeniedError(path, '写入');\r\n }\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `写入文件失败: ${error.message}`, { path });\r\n }\r\n }\r\n\r\n static listDir(path: string): string[] {\r\n try {\r\n return readdirSync(path);\r\n } catch (error: any) {\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `列出目录失败: ${error.message}`, { path });\r\n }\r\n }\r\n\r\n static normalizePath(path: string): string {\r\n return normalize(path).replace(/\\\\/g, '/');\r\n }\r\n\r\n static shouldSkipBySpecs(path: string): boolean {\r\n const base = basename(path);\r\n return base === '.' || base === '..';\r\n }\r\n\r\n static getFileSize(path: string): number {\r\n if (!existsSync(path)) return 0;\r\n if (FileSystem.isfile(path)) return FileSystem.getsize(path);\r\n if (FileSystem.isdir(path)) {\r\n let total = 0;\r\n try {\r\n const entries = readdirSync(path, { withFileTypes: true });\r\n for (const entry of entries) {\r\n const fullPath = join(path, entry.name);\r\n try {\r\n if (entry.isDirectory()) {\r\n total += this.getFileSize(fullPath);\r\n } else if (entry.isFile()) {\r\n total += FileSystem.getsize(fullPath);\r\n }\r\n } catch {}\r\n }\r\n } catch {}\r\n return total;\r\n }\r\n return 0;\r\n }\r\n}\r\n","/**\r\n * TrashInfo 文件解析和处理\r\n */\r\n\r\nimport { join, basename } from 'path';\r\nimport { FileSystem } from './fs.js';\r\nimport { ErrorCode, type TrashedFileInfo } from './types.js';\r\nimport { SafeRmError } from './errors.js';\r\n\r\nexport function validateOperator(operator: string | null | undefined): void {\r\n if (operator === null || operator === undefined) return;\r\n if (typeof operator !== 'string') {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, '操作者名称必须是字符串');\r\n }\r\n if (!operator) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, '操作者名称不能为空');\r\n }\r\n const illegalChars = '<>:\"|?*\\\\/()[]{}';\r\n for (const char of illegalChars) {\r\n if (operator.includes(char)) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, `操作者名称包含非法字符: '${char}'`, { operator, illegal_char: char });\r\n }\r\n }\r\n if (operator.length > 64) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_OPERATOR, '操作者名称过长(最大 64 字符)', { operator, length: operator.length });\r\n }\r\n}\r\n\r\nexport function encodePath(path: string): string {\r\n const normalized = FileSystem.normalizePath(path);\r\n return encodeURIComponent(normalized).replace(/%2F/g, '/');\r\n}\r\n\r\nexport function decodePath(encoded: string): string {\r\n try {\r\n return decodeURIComponent(encoded);\r\n } catch {\r\n return encoded;\r\n }\r\n}\r\n\r\nexport function generateTrashName(originalPath: string): string {\r\n const base = basename(originalPath);\r\n const extIndex = base.lastIndexOf('.');\r\n let name: string;\r\n let ext: string;\r\n\r\n if (extIndex > 0) {\r\n name = base.substring(0, extIndex);\r\n ext = base.substring(extIndex);\r\n } else {\r\n name = base;\r\n ext = '';\r\n }\r\n\r\n const now = new Date();\r\n const timestamp = now.getFullYear().toString() +\r\n (now.getMonth() + 1).toString().padStart(2, '0') +\r\n now.getDate().toString().padStart(2, '0') +\r\n now.getHours().toString().padStart(2, '0') +\r\n now.getMinutes().toString().padStart(2, '0') +\r\n now.getSeconds().toString().padStart(2, '0');\r\n\r\n const randomSuffix = Math.floor(Math.random() * 9000) + 1000;\r\n\r\n return `${name}_${timestamp}_${randomSuffix}${ext}`;\r\n}\r\n\r\nexport function createTrashInfo(\r\n originalPath: string,\r\n trashName: string,\r\n trashDir: string,\r\n operator: string | null\r\n): string {\r\n const infoDir = join(trashDir, 'info');\r\n const infoPath = join(infoDir, `${trashName}.trashinfo`);\r\n const encodedPath = encodePath(originalPath);\r\n const now = new Date();\r\n const deletionDate = now.toISOString().replace(/\\.\\d{3}Z$/, '');\r\n\r\n let content = `[Trash Info]\r\nPath=${encodedPath}\r\nDeletionDate=${deletionDate}\r\n`;\r\n if (operator) {\r\n content += `Operator=${operator}\\n`;\r\n }\r\n\r\n FileSystem.writeFile(infoPath, content);\r\n return infoPath;\r\n}\r\n\r\nexport function parseTrashInfo(infoPath: string, trashDir: string): TrashedFileInfo | null {\r\n try {\r\n const content = FileSystem.readFile(infoPath);\r\n let originalPath: string | null = null;\r\n let deletionDate: string | null = null;\r\n let operator: string | null = null;\r\n\r\n for (const line of content.split('\\n')) {\r\n const trimmed = line.trim();\r\n if (trimmed.startsWith('Path=')) {\r\n originalPath = decodePath(trimmed.substring(5));\r\n } else if (trimmed.startsWith('DeletionDate=')) {\r\n deletionDate = trimmed.substring(13);\r\n } else if (trimmed.startsWith('Operator=')) {\r\n operator = trimmed.substring(9);\r\n }\r\n }\r\n\r\n if (!originalPath || !deletionDate) return null;\r\n\r\n try {\r\n FileSystem.validatePath(originalPath);\r\n } catch {\r\n return null;\r\n }\r\n\r\n const infoBasename = basename(infoPath);\r\n const trashBasename = infoBasename.replace(/\\.trashinfo$/, '');\r\n const trashFile = join(trashDir, 'files', trashBasename);\r\n\r\n if (!FileSystem.exists(trashFile)) return null;\r\n\r\n const size = FileSystem.getFileSize(trashFile);\r\n\r\n return {\r\n index: -1,\r\n originalPath,\r\n deletionDate,\r\n operator,\r\n trashFile,\r\n infoFile: infoPath,\r\n size,\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport function parseRmCommand(rmCommand: string): {\r\n files: string[];\r\n force: boolean;\r\n interactive: boolean;\r\n recursive: boolean;\r\n} {\r\n const trimmed = rmCommand.trim();\r\n if (!trimmed) {\r\n return { files: [], force: false, interactive: false, recursive: false };\r\n }\r\n\r\n const parts: string[] = [];\r\n let current = '';\r\n let inQuote = false;\r\n let quoteChar = '';\r\n\r\n for (let i = 0; i < trimmed.length; i++) {\r\n const char = trimmed[i];\r\n const nextChar = trimmed[i + 1] || '';\r\n\r\n if (inQuote) {\r\n if (char === quoteChar) {\r\n if (nextChar === quoteChar) {\r\n current += char;\r\n i++;\r\n } else {\r\n inQuote = false;\r\n }\r\n } else {\r\n current += char;\r\n }\r\n } else if (char === '\"' || char === \"'\" || char === '`') {\r\n inQuote = true;\r\n quoteChar = char;\r\n } else if (char === ' ' || char === '\\t') {\r\n if (current) {\r\n parts.push(current);\r\n current = '';\r\n }\r\n } else {\r\n current += char;\r\n }\r\n }\r\n\r\n if (current) parts.push(current);\r\n\r\n const files: string[] = [];\r\n let force = false;\r\n let interactive = false;\r\n let recursive = false;\r\n\r\n for (let part of parts) {\r\n if ((part.startsWith('\"') && part.endsWith('\"')) ||\r\n (part.startsWith(\"'\") && part.endsWith(\"'\")) ||\r\n (part.startsWith('`') && part.endsWith('`'))) {\r\n part = part.slice(1, -1);\r\n }\r\n if (!part) continue;\r\n if (part.startsWith('-')) {\r\n if (part.includes('f') || part === '--force') force = true;\r\n if (part.includes('i') || part === '--interactive') interactive = true;\r\n if (part.includes('r') || part.includes('R') || part === '--recursive') recursive = true;\r\n } else {\r\n files.push(part);\r\n }\r\n }\r\n\r\n return { files, force, interactive, recursive };\r\n}\r\n","/**\r\n * SafeRm - 安全删除核心逻辑\r\n */\r\n\r\nimport { isAbsolute, join, resolve } from 'path';\r\nimport { FileSystem } from './fs.js';\r\nimport { validateOperator, generateTrashName, createTrashInfo, parseRmCommand } from './trashinfo.js';\r\nimport {\r\n SafeRmError,\r\n throwNoFilesSpecifiedError,\r\n throwFileNotFoundError,\r\n} from './errors.js';\r\nimport { ErrorCode, Mode, TrashResult, type SafeRmOptions, type SafeRmResult } from './types.js';\r\n\r\nexport class SafeRm {\r\n readonly trashDir: string;\r\n readonly workingDir: string | null;\r\n readonly operator: string | null;\r\n readonly verbose: number;\r\n\r\n constructor(options: SafeRmOptions) {\r\n this.trashDir = resolve(options.trashDir);\r\n this.workingDir = options.workingDir || null;\r\n this.operator = options.operator || null;\r\n this.verbose = options.verbose || 0;\r\n validateOperator(this.operator);\r\n this._ensureTrashDir();\r\n }\r\n\r\n private _ensureTrashDir(): void {\r\n const parentDir = join(this.trashDir, '..');\r\n if (!FileSystem.exists(parentDir)) {\r\n throw new SafeRmError(ErrorCode.ERROR_NO_TRASH_DIR, '垃圾目录的父目录不存在', { trash_dir: this.trashDir, parent_dir: parentDir });\r\n }\r\n FileSystem.makedirs(this.trashDir);\r\n FileSystem.makedirs(join(this.trashDir, 'info'));\r\n FileSystem.makedirs(join(this.trashDir, 'files'));\r\n\r\n const testFile = join(this.trashDir, 'info', '.write_test');\r\n try {\r\n FileSystem.writeFile(testFile, 'test');\r\n FileSystem.remove(testFile);\r\n } catch {\r\n throw new SafeRmError(ErrorCode.ERROR_TRASH_NOT_WRITABLE, '垃圾目录不可写', { trash_dir: this.trashDir });\r\n }\r\n }\r\n\r\n trashSingle(path: string, mode: Mode): TrashResult {\r\n FileSystem.validatePath(path);\r\n if (FileSystem.shouldSkipBySpecs(path)) return TrashResult.FAILURE;\r\n\r\n const absPath = resolve(path);\r\n if (FileSystem.isSystemFile(absPath)) {\r\n throw new SafeRmError(ErrorCode.ERROR_SYSTEM_FILE, '系统文件受保护,无法删除', { path });\r\n }\r\n\r\n if (!FileSystem.lexists(path)) {\r\n if (mode === Mode.MODE_FORCE) return TrashResult.SUCCESS;\r\n throwFileNotFoundError(path);\r\n }\r\n\r\n try {\r\n const trashName = generateTrashName(path);\r\n const destPath = join(this.trashDir, 'files', trashName);\r\n createTrashInfo(path, trashName, this.trashDir, this.operator);\r\n FileSystem.move(path, destPath);\r\n return TrashResult.SUCCESS;\r\n } catch (error) {\r\n if (error instanceof SafeRmError) throw error;\r\n throw new SafeRmError(ErrorCode.ERROR_UNKNOWN, `删除文件失败: ${error instanceof Error ? error.message : String(error)}`, { path });\r\n }\r\n }\r\n\r\n trashAll(paths: string[], mode: Mode): SafeRmResult {\r\n const failedPaths: string[] = [];\r\n const movedPaths: string[] = [];\r\n\r\n for (const path of paths) {\r\n try {\r\n const result = this.trashSingle(path, mode);\r\n if (result === TrashResult.SUCCESS) {\r\n movedPaths.push(path);\r\n } else {\r\n failedPaths.push(path);\r\n }\r\n } catch {\r\n failedPaths.push(path);\r\n }\r\n }\r\n\r\n return {\r\n success: failedPaths.length === 0,\r\n moved: movedPaths,\r\n failed: failedPaths,\r\n movedCount: movedPaths.length,\r\n failedCount: failedPaths.length,\r\n };\r\n }\r\n}\r\n\r\nexport function safeRm(\r\n workingDir: string | null,\r\n rmCommand: string,\r\n trashDir: string,\r\n operator: string | null = null,\r\n verbose: number = 0\r\n): SafeRmResult {\r\n validateOperator(operator);\r\n const { files: parsedFiles, force, interactive } = parseRmCommand(rmCommand);\r\n const files = parsedFiles.map((file) => {\r\n if (!workingDir || isAbsolute(file)) {\r\n return file;\r\n }\r\n return resolve(workingDir, file);\r\n });\r\n\r\n if (files.length === 0) throwNoFilesSpecifiedError();\r\n\r\n let mode: Mode;\r\n if (force) mode = Mode.MODE_FORCE;\r\n else if (interactive) mode = Mode.MODE_INTERACTIVE;\r\n else mode = Mode.MODE_UNSPECIFIED;\r\n\r\n if (workingDir) {\r\n if (!FileSystem.exists(workingDir)) {\r\n throw new SafeRmError(ErrorCode.ERROR_WORKING_DIR_NOT_FOUND, '工作目录不存在', { working_dir: workingDir });\r\n }\r\n }\r\n\r\n const safeRmInstance = new SafeRm({ trashDir, workingDir, operator, verbose });\r\n return safeRmInstance.trashAll(files, mode);\r\n}\r\n","/**\r\n * SafeRestore - 安全恢复核心逻辑\r\n */\r\n\r\nimport { join, resolve, dirname } from 'path';\r\nimport { FileSystem } from './fs.js';\r\nimport { validateOperator, parseTrashInfo } from './trashinfo.js';\r\nimport { SafeRmError, throwNoTrashDirError } from './errors.js';\r\nimport { ErrorCode, type RestoreOptions, type RestoreTarget, type RestoreResult, type ListResult, type TrashedFileInfo } from './types.js';\r\n\r\nfunction matchesPattern(actualPath: string, pattern: string): boolean {\r\n const regexPattern = pattern\r\n .replace(/\\./g, '\\\\.')\r\n .replace(/\\*/g, '.*')\r\n .replace(/\\?/g, '.');\r\n const regex = new RegExp(`^${regexPattern}$`, 'i');\r\n return regex.test(actualPath) || regex.test(actualPath.replace(/\\//g, '\\\\'));\r\n}\r\n\r\nfunction parseIndexSpec(spec: string): number[] {\r\n const indices: number[] = [];\r\n for (const part of spec.split(',')) {\r\n const trimmed = part.trim();\r\n if (trimmed.includes('-')) {\r\n const [startStr, endStr] = trimmed.split('-', 2);\r\n const start = parseInt(startStr, 10);\r\n const end = parseInt(endStr, 10);\r\n if (isNaN(start) || isNaN(end)) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_INDEX, `无效的索引范围: ${trimmed}`, { spec });\r\n }\r\n for (let i = start; i <= end; i++) indices.push(i);\r\n } else {\r\n const index = parseInt(trimmed, 10);\r\n if (isNaN(index)) {\r\n throw new SafeRmError(ErrorCode.ERROR_INVALID_INDEX, `无效的索引: ${trimmed}`, { spec });\r\n }\r\n indices.push(index);\r\n }\r\n }\r\n return indices;\r\n}\r\n\r\nexport class SafeRestore {\r\n readonly trashDir: string;\r\n readonly operator: string | null;\r\n readonly verbose: number;\r\n\r\n constructor(options: RestoreOptions) {\r\n this.trashDir = resolve(options.trashDir);\r\n this.operator = options.operator || null;\r\n this.verbose = options.verbose || 0;\r\n validateOperator(this.operator);\r\n if (!FileSystem.exists(this.trashDir)) throwNoTrashDirError(this.trashDir);\r\n }\r\n\r\n private _scanInfoFiles(): TrashedFileInfo[] {\r\n const infoDir = join(this.trashDir, 'info');\r\n const files: TrashedFileInfo[] = [];\r\n if (!FileSystem.exists(infoDir)) return files;\r\n\r\n const entries = FileSystem.listDir(infoDir);\r\n for (const filename of entries) {\r\n if (!filename.endsWith('.trashinfo')) continue;\r\n const infoPath = join(infoDir, filename);\r\n const info = parseTrashInfo(infoPath, this.trashDir);\r\n if (info && (this.operator === null || info.operator === this.operator)) {\r\n files.push(info);\r\n }\r\n }\r\n return files;\r\n }\r\n\r\n listTrashed(): TrashedFileInfo[] {\r\n const files = this._scanInfoFiles();\r\n files.sort((a, b) => a.deletionDate.localeCompare(b.deletionDate));\r\n for (let i = 0; i < files.length; i++) files[i] = { ...files[i], index: i };\r\n return files;\r\n }\r\n\r\n private _matchesTarget(info: TrashedFileInfo, target: number | string): boolean {\r\n if (typeof target === 'number') return info.index === target;\r\n return matchesPattern(info.originalPath, target) || matchesPattern(info.originalPath.replace(/\\//g, '\\\\'), target);\r\n }\r\n\r\n restore(target: RestoreTarget, overwrite: boolean = false, dryRun: boolean = false): RestoreResult {\r\n const allFiles = this.listTrashed();\r\n const restored: string[] = [];\r\n const failed: Array<{ path: string; error: string }> = [];\r\n const skipped: string[] = [];\r\n\r\n const targets: Array<number | string> = Array.isArray(target) ? target : [target];\r\n const filesToRestore = new Set<TrashedFileInfo>();\r\n\r\n for (const t of targets) {\r\n let matched = false;\r\n for (const info of allFiles) {\r\n if (this._matchesTarget(info, t)) {\r\n filesToRestore.add(info);\r\n matched = true;\r\n }\r\n }\r\n if (!matched && typeof t === 'number') {\r\n failed.push({ path: `index:${t}`, error: '索引没有找到对应的文件' });\r\n }\r\n }\r\n\r\n for (const info of Array.from(filesToRestore)) {\r\n try {\r\n if (FileSystem.exists(info.originalPath) && !overwrite) {\r\n failed.push({ path: info.originalPath, error: 'file already exists' });\r\n continue;\r\n }\r\n const parentDir = dirname(info.originalPath);\r\n if (parentDir && !FileSystem.exists(parentDir)) FileSystem.makedirs(parentDir);\r\n\r\n if (dryRun) {\r\n restored.push(info.originalPath);\r\n } else {\r\n FileSystem.move(info.trashFile, info.originalPath);\r\n FileSystem.remove(info.infoFile);\r\n restored.push(info.originalPath);\r\n }\r\n } catch (error) {\r\n failed.push({ path: info.originalPath, error: error instanceof Error ? error.message : String(error) });\r\n }\r\n }\r\n\r\n return {\r\n success: failed.length === 0,\r\n restored,\r\n failed,\r\n skipped,\r\n restoredCount: restored.length,\r\n failedCount: failed.length,\r\n skippedCount: skipped.length,\r\n };\r\n }\r\n}\r\n\r\nexport function listTrashed(trashDir: string, operator: string | null = null): ListResult {\r\n validateOperator(operator);\r\n const restorer = new SafeRestore({ trashDir, operator });\r\n const infoList = restorer.listTrashed();\r\n return { success: true, total: infoList.length, files: infoList };\r\n}\r\n\r\nexport function restore(\r\n trashDir: string,\r\n target: RestoreTarget,\r\n operator: string | null = null,\r\n overwrite: boolean = false,\r\n dryRun: boolean = false,\r\n parseIndexRanges: boolean = true\r\n): RestoreResult {\r\n validateOperator(operator);\r\n const restorer = new SafeRestore({ trashDir, operator });\r\n\r\n let actualTarget: RestoreTarget = target;\r\n if (parseIndexRanges && typeof target === 'string') {\r\n if (/^[\\d,\\s\\-]+$/.test(target.trim())) {\r\n try {\r\n actualTarget = parseIndexSpec(target);\r\n } catch {\r\n actualTarget = target;\r\n }\r\n }\r\n }\r\n\r\n return restorer.restore(actualTarget, overwrite, dryRun);\r\n}\r\n"],"mappings":"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","names":["resolve","path","quoted","resolve","execSync","existsSync","createTool","existsSync","execSync","DEFAULT_TIMEOUT_MS","MAX_TIMEOUT_MS","createTool","join","createTool","path","existsSync","join","dirname","path","existsSync","dirname","join","join","basename","path","basename","join","join","join","path","join","resolve","dirname","resolve","join","dirname","join","createTool","DEFAULT_TIMEOUT_MS","MAX_TIMEOUT_MS","resolve"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/templates/bash.render.ts"],"sourcesContent":["/**\n * Bash 工具渲染模板\n */\n\nimport type { InlineRenderTemplate } from '@agentdevjs/core';\n\nfunction escapeHtml(text: unknown): string {\n const str = String(text);\n const map: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;'\n };\n return str.replace(/[&<>\"']/g, m => map[m]!);\n}\n\nfunction formatOutput(data: unknown): string {\n if (data === null || data === undefined) return '';\n if (typeof data === 'string') return data;\n if (typeof data === 'object') {\n if (data instanceof Error) return data.message;\n const obj = data as Record<string, unknown>;\n if ('stdout' in obj || 'stderr' in obj) {\n return String(obj.stdout || '') + String(obj.stderr || '');\n }\n if ('error' in obj) {\n const errorValue = obj.error;\n if (typeof errorValue === 'string') return errorValue;\n if (errorValue instanceof Error) return errorValue.message;\n return JSON.stringify(data, null, 2);\n }\n return JSON.stringify(data, null, 2);\n }\n return String(data);\n}\n\nfunction formatError(data: unknown): string {\n const text = formatOutput(data);\n return `<div class=\"tool-error\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>\n <span>${escapeHtml(text)}</span>\n </div>`;\n}\n\n/**\n * 执行中进度数据(工单 025):宿主经 call 模板第三参注入。\n * 缺省时不渲染任何进度节点,call 态与既有行为一致(降级安全)。\n */\nexport interface BashCallProgressContext {\n /** 工具开始执行的 Unix 毫秒时间戳 */\n startedAt?: number;\n /** 已执行时长(毫秒);缺省时由 startedAt 插值 */\n elapsedMs?: number;\n /** 本次调用生效超时(毫秒);null/undefined 不显示超时上限 */\n timeoutMs?: number | null;\n /** 输出尾部文本(宿主截尾,建议 5 行) */\n outputTail?: string;\n}\n\nfunction isZhUi(): boolean {\n try {\n return String(navigator.language || '').toLowerCase().startsWith('zh');\n } catch {\n return true;\n }\n}\n\n/** 紧凑时长:12s / 2m05s / 1h02m */\nfunction formatDurationCompact(ms: number): string {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n if (minutes < 60) {\n return seconds > 0 ? `${minutes}m${String(seconds).padStart(2, '0')}s` : `${minutes}m`;\n }\n const hours = Math.floor(minutes / 60);\n return `${hours}h${String(minutes % 60).padStart(2, '0')}m`;\n}\n\n/** 超时上限展示:90s / 2m / 1.5m */\nfunction formatTimeoutCompact(ms: number): string {\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`;\n const minutes = ms / 60_000;\n return Number.isInteger(minutes) ? `${minutes}m` : `${minutes.toFixed(1)}m`;\n}\n\n/**\n * 渲染执行中进度块:「已运行 Ns · 超时 Xm」+ 尾部输出预览\n * (等宽小字、限 5 行高、overflow 隐藏、直接可见不折叠)。\n * 结果落地后宿主移除该块,完成态不保留 tail。\n */\nexport function renderCallProgress(progress: BashCallProgressContext): string {\n const elapsedMs = typeof progress.elapsedMs === 'number' && progress.elapsedMs >= 0\n ? progress.elapsedMs\n : (typeof progress.startedAt === 'number' && progress.startedAt > 0\n ? Math.max(0, Date.now() - progress.startedAt)\n : null);\n const timeoutMs = typeof progress.timeoutMs === 'number' && progress.timeoutMs > 0\n ? progress.timeoutMs\n : null;\n\n const zh = isZhUi();\n const parts: string[] = [];\n if (elapsedMs !== null) {\n parts.push(zh ? `已运行 ${formatDurationCompact(elapsedMs)}` : `running ${formatDurationCompact(elapsedMs)}`);\n }\n if (timeoutMs !== null) {\n parts.push(zh ? `超时 ${formatTimeoutCompact(timeoutMs)}` : `timeout ${formatTimeoutCompact(timeoutMs)}`);\n }\n\n let html = '<div class=\"bash-progress-live\">';\n if (parts.length > 0) {\n html += `<div class=\"bash-progress-meta\"><span class=\"bash-progress-dot\"></span>${escapeHtml(parts.join(' · '))}</div>`;\n }\n const tail = typeof progress.outputTail === 'string' ? progress.outputTail.replace(/\\s+$/, '') : '';\n if (tail) {\n html += `<pre class=\"bash-progress-tail\">${escapeHtml(tail)}</pre>`;\n }\n html += '</div>';\n return html;\n}\n\nconst bashRender: InlineRenderTemplate = {\n call: (args: { command?: string }, _success?: boolean, progress?: BashCallProgressContext) => {\n const command = args?.command || '';\n const progressHtml = progress && typeof progress === 'object' ? renderCallProgress(progress) : '';\n return `<div class=\"bash-command\">> ${escapeHtml(command)}</div>${progressHtml}`;\n },\n result: (data: unknown, success?: boolean) => {\n if (!success) return formatError(data);\n const output = formatOutput(data);\n return `<pre class=\"bash-output\">${escapeHtml(output)}</pre>`;\n }\n};\n\nexport default bashRender;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/templates/bash.render.ts"],"sourcesContent":["/**\r\n * Bash 工具渲染模板\r\n */\r\n\r\nimport type { InlineRenderTemplate } from '@agentdevjs/core';\r\n\r\nfunction escapeHtml(text: unknown): string {\r\n const str = String(text);\r\n const map: Record<string, string> = {\r\n '&': '&amp;',\r\n '<': '&lt;',\r\n '>': '&gt;',\r\n '\"': '&quot;',\r\n \"'\": '&#39;'\r\n };\r\n return str.replace(/[&<>\"']/g, m => map[m]!);\r\n}\r\n\r\nfunction formatOutput(data: unknown): string {\r\n if (data === null || data === undefined) return '';\r\n if (typeof data === 'string') return data;\r\n if (typeof data === 'object') {\r\n if (data instanceof Error) return data.message;\r\n const obj = data as Record<string, unknown>;\r\n if ('stdout' in obj || 'stderr' in obj) {\r\n return String(obj.stdout || '') + String(obj.stderr || '');\r\n }\r\n if ('error' in obj) {\r\n const errorValue = obj.error;\r\n if (typeof errorValue === 'string') return errorValue;\r\n if (errorValue instanceof Error) return errorValue.message;\r\n return JSON.stringify(data, null, 2);\r\n }\r\n return JSON.stringify(data, null, 2);\r\n }\r\n return String(data);\r\n}\r\n\r\nfunction formatError(data: unknown): string {\r\n const text = formatOutput(data);\r\n return `<div class=\"tool-error\">\r\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>\r\n <span>${escapeHtml(text)}</span>\r\n </div>`;\r\n}\r\n\r\n/**\r\n * 执行中进度数据(工单 025):宿主经 call 模板第三参注入。\r\n * 缺省时不渲染任何进度节点,call 态与既有行为一致(降级安全)。\r\n */\r\nexport interface BashCallProgressContext {\r\n /** 工具开始执行的 Unix 毫秒时间戳 */\r\n startedAt?: number;\r\n /** 已执行时长(毫秒);缺省时由 startedAt 插值 */\r\n elapsedMs?: number;\r\n /** 本次调用生效超时(毫秒);null/undefined 不显示超时上限 */\r\n timeoutMs?: number | null;\r\n /** 输出尾部文本(宿主截尾,建议 5 行) */\r\n outputTail?: string;\r\n}\r\n\r\nfunction isZhUi(): boolean {\r\n try {\r\n return String(navigator.language || '').toLowerCase().startsWith('zh');\r\n } catch {\r\n return true;\r\n }\r\n}\r\n\r\n/** 紧凑时长:12s / 2m05s / 1h02m */\r\nfunction formatDurationCompact(ms: number): string {\r\n const totalSeconds = Math.max(0, Math.floor(ms / 1000));\r\n if (totalSeconds < 60) return `${totalSeconds}s`;\r\n const minutes = Math.floor(totalSeconds / 60);\r\n const seconds = totalSeconds % 60;\r\n if (minutes < 60) {\r\n return seconds > 0 ? `${minutes}m${String(seconds).padStart(2, '0')}s` : `${minutes}m`;\r\n }\r\n const hours = Math.floor(minutes / 60);\r\n return `${hours}h${String(minutes % 60).padStart(2, '0')}m`;\r\n}\r\n\r\n/** 超时上限展示:90s / 2m / 1.5m */\r\nfunction formatTimeoutCompact(ms: number): string {\r\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`;\r\n const minutes = ms / 60_000;\r\n return Number.isInteger(minutes) ? `${minutes}m` : `${minutes.toFixed(1)}m`;\r\n}\r\n\r\n/**\r\n * 渲染执行中进度块:「已运行 Ns · 超时 Xm」+ 尾部输出预览\r\n * (等宽小字、限 5 行高、overflow 隐藏、直接可见不折叠)。\r\n * 结果落地后宿主移除该块,完成态不保留 tail。\r\n */\r\nexport function renderCallProgress(progress: BashCallProgressContext): string {\r\n const elapsedMs = typeof progress.elapsedMs === 'number' && progress.elapsedMs >= 0\r\n ? progress.elapsedMs\r\n : (typeof progress.startedAt === 'number' && progress.startedAt > 0\r\n ? Math.max(0, Date.now() - progress.startedAt)\r\n : null);\r\n const timeoutMs = typeof progress.timeoutMs === 'number' && progress.timeoutMs > 0\r\n ? progress.timeoutMs\r\n : null;\r\n\r\n const zh = isZhUi();\r\n const parts: string[] = [];\r\n if (elapsedMs !== null) {\r\n parts.push(zh ? `已运行 ${formatDurationCompact(elapsedMs)}` : `running ${formatDurationCompact(elapsedMs)}`);\r\n }\r\n if (timeoutMs !== null) {\r\n parts.push(zh ? `超时 ${formatTimeoutCompact(timeoutMs)}` : `timeout ${formatTimeoutCompact(timeoutMs)}`);\r\n }\r\n\r\n let html = '<div class=\"bash-progress-live\">';\r\n if (parts.length > 0) {\r\n html += `<div class=\"bash-progress-meta\"><span class=\"bash-progress-dot\"></span>${escapeHtml(parts.join(' · '))}</div>`;\r\n }\r\n const tail = typeof progress.outputTail === 'string' ? progress.outputTail.replace(/\\s+$/, '') : '';\r\n if (tail) {\r\n html += `<pre class=\"bash-progress-tail\">${escapeHtml(tail)}</pre>`;\r\n }\r\n html += '</div>';\r\n return html;\r\n}\r\n\r\nconst bashRender: InlineRenderTemplate = {\r\n call: (args: { command?: string }, _success?: boolean, progress?: BashCallProgressContext) => {\r\n const command = args?.command || '';\r\n const progressHtml = progress && typeof progress === 'object' ? renderCallProgress(progress) : '';\r\n return `<div class=\"bash-command\">> ${escapeHtml(command)}</div>${progressHtml}`;\r\n },\r\n result: (data: unknown, success?: boolean) => {\r\n if (!success) return formatError(data);\r\n const output = formatOutput(data);\r\n return `<pre class=\"bash-output\">${escapeHtml(output)}</pre>`;\r\n }\r\n};\r\n\r\nexport default bashRender;\r\n"],"mappings":";AAMA,SAAS,WAAW,MAAuB;AACzC,QAAM,MAAM,OAAO,IAAI;AACvB,QAAM,MAA8B;AAAA,IAClC,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACA,SAAO,IAAI,QAAQ,YAAY,OAAK,IAAI,CAAC,CAAE;AAC7C;AAEA,SAAS,aAAa,MAAuB;AAC3C,MAAI,SAAS,QAAQ,SAAS,OAAW,QAAO;AAChD,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI,OAAO,SAAS,UAAU;AAC5B,QAAI,gBAAgB,MAAO,QAAO,KAAK;AACvC,UAAM,MAAM;AACZ,QAAI,YAAY,OAAO,YAAY,KAAK;AACtC,aAAO,OAAO,IAAI,UAAU,EAAE,IAAI,OAAO,IAAI,UAAU,EAAE;AAAA,IAC3D;AACA,QAAI,WAAW,KAAK;AAClB,YAAM,aAAa,IAAI;AACvB,UAAI,OAAO,eAAe,SAAU,QAAO;AAC3C,UAAI,sBAAsB,MAAO,QAAO,WAAW;AACnD,aAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AAAA,IACrC;AACA,WAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AAAA,EACrC;AACA,SAAO,OAAO,IAAI;AACpB;AAEA,SAAS,YAAY,MAAuB;AAC1C,QAAM,OAAO,aAAa,IAAI;AAC9B,SAAO;AAAA;AAAA,YAEG,WAAW,IAAI,CAAC;AAAA;AAE5B;AAiBA,SAAS,SAAkB;AACzB,MAAI;AACF,WAAO,OAAO,UAAU,YAAY,EAAE,EAAE,YAAY,EAAE,WAAW,IAAI;AAAA,EACvE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,sBAAsB,IAAoB;AACjD,QAAM,eAAe,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,GAAI,CAAC;AACtD,MAAI,eAAe,GAAI,QAAO,GAAG,YAAY;AAC7C,QAAM,UAAU,KAAK,MAAM,eAAe,EAAE;AAC5C,QAAM,UAAU,eAAe;AAC/B,MAAI,UAAU,IAAI;AAChB,WAAO,UAAU,IAAI,GAAG,OAAO,IAAI,OAAO,OAAO,EAAE,SAAS,GAAG,GAAG,CAAC,MAAM,GAAG,OAAO;AAAA,EACrF;AACA,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AACrC,SAAO,GAAG,KAAK,IAAI,OAAO,UAAU,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AAC1D;AAGA,SAAS,qBAAqB,IAAoB;AAChD,MAAI,KAAK,IAAQ,QAAO,GAAG,KAAK,MAAM,KAAK,GAAI,CAAC;AAChD,QAAM,UAAU,KAAK;AACrB,SAAO,OAAO,UAAU,OAAO,IAAI,GAAG,OAAO,MAAM,GAAG,QAAQ,QAAQ,CAAC,CAAC;AAC1E;AAOO,SAAS,mBAAmB,UAA2C;AAC5E,QAAM,YAAY,OAAO,SAAS,cAAc,YAAY,SAAS,aAAa,IAC9E,SAAS,YACR,OAAO,SAAS,cAAc,YAAY,SAAS,YAAY,IAC9D,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,SAAS,SAAS,IAC3C;AACN,QAAM,YAAY,OAAO,SAAS,cAAc,YAAY,SAAS,YAAY,IAC7E,SAAS,YACT;AAEJ,QAAM,KAAK,OAAO;AAClB,QAAM,QAAkB,CAAC;AACzB,MAAI,cAAc,MAAM;AACtB,UAAM,KAAK,KAAK,sBAAO,sBAAsB,SAAS,CAAC,KAAK,WAAW,sBAAsB,SAAS,CAAC,EAAE;AAAA,EAC3G;AACA,MAAI,cAAc,MAAM;AACtB,UAAM,KAAK,KAAK,gBAAM,qBAAqB,SAAS,CAAC,KAAK,WAAW,qBAAqB,SAAS,CAAC,EAAE;AAAA,EACxG;AAEA,MAAI,OAAO;AACX,MAAI,MAAM,SAAS,GAAG;AACpB,YAAQ,0EAA0E,WAAW,MAAM,KAAK,QAAK,CAAC,CAAC;AAAA,EACjH;AACA,QAAM,OAAO,OAAO,SAAS,eAAe,WAAW,SAAS,WAAW,QAAQ,QAAQ,EAAE,IAAI;AACjG,MAAI,MAAM;AACR,YAAQ,mCAAmC,WAAW,IAAI,CAAC;AAAA,EAC7D;AACA,UAAQ;AACR,SAAO;AACT;AAEA,IAAM,aAAmC;AAAA,EACvC,MAAM,CAAC,MAA4B,UAAoB,aAAuC;AAC5F,UAAM,UAAU,MAAM,WAAW;AACjC,UAAM,eAAe,YAAY,OAAO,aAAa,WAAW,mBAAmB,QAAQ,IAAI;AAC/F,WAAO,+BAA+B,WAAW,OAAO,CAAC,SAAS,YAAY;AAAA,EAChF;AAAA,EACA,QAAQ,CAAC,MAAe,YAAsB;AAC5C,QAAI,CAAC,QAAS,QAAO,YAAY,IAAI;AACrC,UAAM,SAAS,aAAa,IAAI;AAChC,WAAO,4BAA4B,WAAW,MAAM,CAAC;AAAA,EACvD;AACF;AAEA,IAAO,sBAAQ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/templates/trash-delete.render.ts"],"sourcesContent":["/**\n * Safe Trash Delete 渲染模板\n */\n\nimport type { InlineRenderTemplate } from '@agentdevjs/core';\n\nfunction escapeHtml(text: unknown): string {\n const str = String(text);\n const map: Record<string, string> = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' };\n return str.replace(/[&<>\"']/g, m => map[m]!);\n}\n\nfunction formatError(data: unknown): string {\n const text = typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data);\n return `<div class=\"tool-error\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg><span>${escapeHtml(text)}</span></div>`;\n}\n\nexport default {\n call: (args: { paths?: string[] }) => {\n const paths = args.paths || [];\n const displayPaths = paths.slice(0, 3).map(p => escapeHtml(p)).join(', ');\n const more = paths.length > 3 ? ` +${paths.length - 3} more` : '';\n return `<div class=\"bash-command\">Safe delete <span class=\"file-path\">${displayPaths}${more}</span></div>`;\n },\n result: (data: { moved_count?: number; failed?: string[] }, success?: boolean) => {\n if (!success) return formatError(data);\n const movedCount = data.moved_count || 0;\n const failed = data.failed || [];\n if (movedCount > 0 && failed.length === 0) {\n return `<div style=\"color:var(--success-color)\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>Moved ${movedCount} item(s) to trash</div>`;\n }\n if (movedCount > 0) {\n let html = `<div style=\"color:var(--warning-color)\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>Moved ${movedCount}, ${failed.length} failed</div>`;\n if (failed.length > 0) html += `<div style=\"font-size:11px;color:var(--error-color);margin-top:4px\">Failed: ${escapeHtml(failed.join(', '))}</div>`;\n return html;\n }\n return `<div style=\"color:var(--text-secondary)\">No files moved</div>`;\n }\n} as const satisfies InlineRenderTemplate;\n"],"mappings":";AAMA,SAAS,WAAW,MAAuB;AACzC,QAAM,MAAM,OAAO,IAAI;AACvB,QAAM,MAA8B,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ;AAC1G,SAAO,IAAI,QAAQ,YAAY,OAAK,IAAI,CAAC,CAAE;AAC7C;AAEA,SAAS,YAAY,MAAuB;AAC1C,QAAM,OAAO,OAAO,SAAS,WAAW,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,OAAO,IAAI;AACnF,SAAO,uNAAuN,WAAW,IAAI,CAAC;AAChP;AAEA,IAAO,8BAAQ;AAAA,EACb,MAAM,CAAC,SAA+B;AACpC,UAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,UAAM,eAAe,MAAM,MAAM,GAAG,CAAC,EAAE,IAAI,OAAK,WAAW,CAAC,CAAC,EAAE,KAAK,IAAI;AACxE,UAAM,OAAO,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,CAAC,UAAU;AAC/D,WAAO,iEAAiE,YAAY,GAAG,IAAI;AAAA,EAC7F;AAAA,EACA,QAAQ,CAAC,MAAmD,YAAsB;AAChF,QAAI,CAAC,QAAS,QAAO,YAAY,IAAI;AACrC,UAAM,aAAa,KAAK,eAAe;AACvC,UAAM,SAAS,KAAK,UAAU,CAAC;AAC/B,QAAI,aAAa,KAAK,OAAO,WAAW,GAAG;AACzC,aAAO,uOAAuO,UAAU;AAAA,IAC1P;AACA,QAAI,aAAa,GAAG;AAClB,UAAI,OAAO,sRAAsR,UAAU,KAAK,OAAO,MAAM;AAC7T,UAAI,OAAO,SAAS,EAAG,SAAQ,+EAA+E,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC;AAC3I,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/templates/trash-delete.render.ts"],"sourcesContent":["/**\r\n * Safe Trash Delete 渲染模板\r\n */\r\n\r\nimport type { InlineRenderTemplate } from '@agentdevjs/core';\r\n\r\nfunction escapeHtml(text: unknown): string {\r\n const str = String(text);\r\n const map: Record<string, string> = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' };\r\n return str.replace(/[&<>\"']/g, m => map[m]!);\r\n}\r\n\r\nfunction formatError(data: unknown): string {\r\n const text = typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data);\r\n return `<div class=\"tool-error\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg><span>${escapeHtml(text)}</span></div>`;\r\n}\r\n\r\nexport default {\r\n call: (args: { paths?: string[] }) => {\r\n const paths = args.paths || [];\r\n const displayPaths = paths.slice(0, 3).map(p => escapeHtml(p)).join(', ');\r\n const more = paths.length > 3 ? ` +${paths.length - 3} more` : '';\r\n return `<div class=\"bash-command\">Safe delete <span class=\"file-path\">${displayPaths}${more}</span></div>`;\r\n },\r\n result: (data: { moved_count?: number; failed?: string[] }, success?: boolean) => {\r\n if (!success) return formatError(data);\r\n const movedCount = data.moved_count || 0;\r\n const failed = data.failed || [];\r\n if (movedCount > 0 && failed.length === 0) {\r\n return `<div style=\"color:var(--success-color)\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>Moved ${movedCount} item(s) to trash</div>`;\r\n }\r\n if (movedCount > 0) {\r\n let html = `<div style=\"color:var(--warning-color)\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>Moved ${movedCount}, ${failed.length} failed</div>`;\r\n if (failed.length > 0) html += `<div style=\"font-size:11px;color:var(--error-color);margin-top:4px\">Failed: ${escapeHtml(failed.join(', '))}</div>`;\r\n return html;\r\n }\r\n return `<div style=\"color:var(--text-secondary)\">No files moved</div>`;\r\n }\r\n} as const satisfies InlineRenderTemplate;\r\n"],"mappings":";AAMA,SAAS,WAAW,MAAuB;AACzC,QAAM,MAAM,OAAO,IAAI;AACvB,QAAM,MAA8B,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ;AAC1G,SAAO,IAAI,QAAQ,YAAY,OAAK,IAAI,CAAC,CAAE;AAC7C;AAEA,SAAS,YAAY,MAAuB;AAC1C,QAAM,OAAO,OAAO,SAAS,WAAW,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,OAAO,IAAI;AACnF,SAAO,uNAAuN,WAAW,IAAI,CAAC;AAChP;AAEA,IAAO,8BAAQ;AAAA,EACb,MAAM,CAAC,SAA+B;AACpC,UAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,UAAM,eAAe,MAAM,MAAM,GAAG,CAAC,EAAE,IAAI,OAAK,WAAW,CAAC,CAAC,EAAE,KAAK,IAAI;AACxE,UAAM,OAAO,MAAM,SAAS,IAAI,KAAK,MAAM,SAAS,CAAC,UAAU;AAC/D,WAAO,iEAAiE,YAAY,GAAG,IAAI;AAAA,EAC7F;AAAA,EACA,QAAQ,CAAC,MAAmD,YAAsB;AAChF,QAAI,CAAC,QAAS,QAAO,YAAY,IAAI;AACrC,UAAM,aAAa,KAAK,eAAe;AACvC,UAAM,SAAS,KAAK,UAAU,CAAC;AAC/B,QAAI,aAAa,KAAK,OAAO,WAAW,GAAG;AACzC,aAAO,uOAAuO,UAAU;AAAA,IAC1P;AACA,QAAI,aAAa,GAAG;AAClB,UAAI,OAAO,sRAAsR,UAAU,KAAK,OAAO,MAAM;AAC7T,UAAI,OAAO,SAAS,EAAG,SAAQ,+EAA+E,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC;AAC3I,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/templates/trash-list.render.ts"],"sourcesContent":["/**\n * Safe Trash List 渲染模板\n */\n\nimport type { InlineRenderTemplate } from '@agentdevjs/core';\n\nfunction escapeHtml(text: unknown): string {\n const str = String(text);\n const map: Record<string, string> = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' };\n return str.replace(/[&<>\"']/g, m => map[m]!);\n}\n\nfunction formatError(data: unknown): string {\n const text = typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data);\n return `<div class=\"tool-error\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg><span>${escapeHtml(text)}</span></div>`;\n}\n\nfunction formatDateTime(dateStr: string): string {\n try {\n const date = new Date(dateStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n if (diffMins < 1) return 'just now';\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n return date.toLocaleDateString();\n } catch { return dateStr; }\n}\n\nexport default {\n call: (args: { trashDir?: string }) => {\n const trashDir = args.trashDir || '.trash';\n return `<div class=\"bash-command\">List trash <span style=\"color:var(--text-secondary);font-size:11px\">(${escapeHtml(trashDir)})</span></div>`;\n },\n result: (data: { total?: number; files?: Array<{ index: number; originalPath: string; deletionDate: string; size_formatted?: string }> }, success?: boolean) => {\n if (!success) return formatError(data);\n const total = data.total || 0;\n const files = data.files || [];\n if (total === 0) {\n return `<div style=\"color:var(--text-secondary);font-style:italic;padding:12px\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px;opacity:0.5\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>Trash is empty</div>`;\n }\n return `<div style=\"font-family:monospace;font-size:12px\"><div style=\"color:var(--text-secondary);margin-bottom:8px\">${total} item(s) in trash</div><table style=\"border-collapse:collapse;width:100%\"><thead><tr style=\"color:var(--text-secondary);font-size:11px\"><th style=\"text-align:left;padding:4px 8px;border-bottom:1px solid var(--border-color)\">#</th><th style=\"text-align:left;padding:4px 8px;border-bottom:1px solid var(--border-color)\">Path</th><th style=\"text-align:left;padding:4px 8px;border-bottom:1px solid var(--border-color)\">Date</th><th style=\"text-align:right;padding:4px 8px;border-bottom:1px solid var(--border-color)\">Size</th></tr></thead><tbody>${files.map(f => `<tr style=\"border-bottom:1px solid var(--border-color)${f.index % 2 === 0 ? '' : ';background:rgba(255,255,255,0.02)'}\"><td style=\"padding:4px 8px;color:var(--accent-color)\">${escapeHtml(String(f.index))}</td><td style=\"padding:4px 8px;max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\" title=\"${escapeHtml(f.originalPath)}\">${escapeHtml(f.originalPath)}</td><td style=\"padding:4px 8px;color:var(--text-secondary)\">${escapeHtml(formatDateTime(f.deletionDate))}</td><td style=\"padding:4px 8px;text-align:right;color:var(--text-secondary)\">${escapeHtml(f.size_formatted || '0 B')}</td></tr>`).join('')}</tbody></table></div>`;\n }\n} as const satisfies InlineRenderTemplate;\n"],"mappings":";AAMA,SAAS,WAAW,MAAuB;AACzC,QAAM,MAAM,OAAO,IAAI;AACvB,QAAM,MAA8B,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ;AAC1G,SAAO,IAAI,QAAQ,YAAY,OAAK,IAAI,CAAC,CAAE;AAC7C;AAEA,SAAS,YAAY,MAAuB;AAC1C,QAAM,OAAO,OAAO,SAAS,WAAW,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,OAAO,IAAI;AACnF,SAAO,uNAAuN,WAAW,IAAI,CAAC;AAChP;AAEA,SAAS,eAAe,SAAyB;AAC/C,MAAI;AACF,UAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,SAAS,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC5C,UAAM,WAAW,KAAK,MAAM,SAAS,GAAK;AAC1C,UAAM,YAAY,KAAK,MAAM,SAAS,IAAO;AAC7C,UAAM,WAAW,KAAK,MAAM,SAAS,KAAQ;AAC7C,QAAI,WAAW,EAAG,QAAO;AACzB,QAAI,WAAW,GAAI,QAAO,GAAG,QAAQ;AACrC,QAAI,YAAY,GAAI,QAAO,GAAG,SAAS;AACvC,QAAI,WAAW,EAAG,QAAO,GAAG,QAAQ;AACpC,WAAO,KAAK,mBAAmB;AAAA,EACjC,QAAQ;AAAE,WAAO;AAAA,EAAS;AAC5B;AAEA,IAAO,4BAAQ;AAAA,EACb,MAAM,CAAC,SAAgC;AACrC,UAAM,WAAW,KAAK,YAAY;AAClC,WAAO,kGAAkG,WAAW,QAAQ,CAAC;AAAA,EAC/H;AAAA,EACA,QAAQ,CAAC,MAAiI,YAAsB;AAC9J,QAAI,CAAC,QAAS,QAAO,YAAY,IAAI;AACrC,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,QAAI,UAAU,GAAG;AACf,aAAO;AAAA,IACT;AACA,WAAO,gHAAgH,KAAK,iiBAAiiB,MAAM,IAAI,OAAK,yDAAyD,EAAE,QAAQ,MAAM,IAAI,KAAK,oCAAoC,2DAA2D,WAAW,OAAO,EAAE,KAAK,CAAC,CAAC,qHAAqH,WAAW,EAAE,YAAY,CAAC,KAAK,WAAW,EAAE,YAAY,CAAC,gEAAgE,WAAW,eAAe,EAAE,YAAY,CAAC,CAAC,iFAAiF,WAAW,EAAE,kBAAkB,KAAK,CAAC,YAAY,EAAE,KAAK,EAAE,CAAC;AAAA,EAC9xC;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/templates/trash-list.render.ts"],"sourcesContent":["/**\r\n * Safe Trash List 渲染模板\r\n */\r\n\r\nimport type { InlineRenderTemplate } from '@agentdevjs/core';\r\n\r\nfunction escapeHtml(text: unknown): string {\r\n const str = String(text);\r\n const map: Record<string, string> = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' };\r\n return str.replace(/[&<>\"']/g, m => map[m]!);\r\n}\r\n\r\nfunction formatError(data: unknown): string {\r\n const text = typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data);\r\n return `<div class=\"tool-error\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg><span>${escapeHtml(text)}</span></div>`;\r\n}\r\n\r\nfunction formatDateTime(dateStr: string): string {\r\n try {\r\n const date = new Date(dateStr);\r\n const now = new Date();\r\n const diffMs = now.getTime() - date.getTime();\r\n const diffMins = Math.floor(diffMs / 60000);\r\n const diffHours = Math.floor(diffMs / 3600000);\r\n const diffDays = Math.floor(diffMs / 86400000);\r\n if (diffMins < 1) return 'just now';\r\n if (diffMins < 60) return `${diffMins}m ago`;\r\n if (diffHours < 24) return `${diffHours}h ago`;\r\n if (diffDays < 7) return `${diffDays}d ago`;\r\n return date.toLocaleDateString();\r\n } catch { return dateStr; }\r\n}\r\n\r\nexport default {\r\n call: (args: { trashDir?: string }) => {\r\n const trashDir = args.trashDir || '.trash';\r\n return `<div class=\"bash-command\">List trash <span style=\"color:var(--text-secondary);font-size:11px\">(${escapeHtml(trashDir)})</span></div>`;\r\n },\r\n result: (data: { total?: number; files?: Array<{ index: number; originalPath: string; deletionDate: string; size_formatted?: string }> }, success?: boolean) => {\r\n if (!success) return formatError(data);\r\n const total = data.total || 0;\r\n const files = data.files || [];\r\n if (total === 0) {\r\n return `<div style=\"color:var(--text-secondary);font-style:italic;padding:12px\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px;opacity:0.5\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>Trash is empty</div>`;\r\n }\r\n return `<div style=\"font-family:monospace;font-size:12px\"><div style=\"color:var(--text-secondary);margin-bottom:8px\">${total} item(s) in trash</div><table style=\"border-collapse:collapse;width:100%\"><thead><tr style=\"color:var(--text-secondary);font-size:11px\"><th style=\"text-align:left;padding:4px 8px;border-bottom:1px solid var(--border-color)\">#</th><th style=\"text-align:left;padding:4px 8px;border-bottom:1px solid var(--border-color)\">Path</th><th style=\"text-align:left;padding:4px 8px;border-bottom:1px solid var(--border-color)\">Date</th><th style=\"text-align:right;padding:4px 8px;border-bottom:1px solid var(--border-color)\">Size</th></tr></thead><tbody>${files.map(f => `<tr style=\"border-bottom:1px solid var(--border-color)${f.index % 2 === 0 ? '' : ';background:rgba(255,255,255,0.02)'}\"><td style=\"padding:4px 8px;color:var(--accent-color)\">${escapeHtml(String(f.index))}</td><td style=\"padding:4px 8px;max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\" title=\"${escapeHtml(f.originalPath)}\">${escapeHtml(f.originalPath)}</td><td style=\"padding:4px 8px;color:var(--text-secondary)\">${escapeHtml(formatDateTime(f.deletionDate))}</td><td style=\"padding:4px 8px;text-align:right;color:var(--text-secondary)\">${escapeHtml(f.size_formatted || '0 B')}</td></tr>`).join('')}</tbody></table></div>`;\r\n }\r\n} as const satisfies InlineRenderTemplate;\r\n"],"mappings":";AAMA,SAAS,WAAW,MAAuB;AACzC,QAAM,MAAM,OAAO,IAAI;AACvB,QAAM,MAA8B,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ;AAC1G,SAAO,IAAI,QAAQ,YAAY,OAAK,IAAI,CAAC,CAAE;AAC7C;AAEA,SAAS,YAAY,MAAuB;AAC1C,QAAM,OAAO,OAAO,SAAS,WAAW,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,OAAO,IAAI;AACnF,SAAO,uNAAuN,WAAW,IAAI,CAAC;AAChP;AAEA,SAAS,eAAe,SAAyB;AAC/C,MAAI;AACF,UAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,SAAS,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC5C,UAAM,WAAW,KAAK,MAAM,SAAS,GAAK;AAC1C,UAAM,YAAY,KAAK,MAAM,SAAS,IAAO;AAC7C,UAAM,WAAW,KAAK,MAAM,SAAS,KAAQ;AAC7C,QAAI,WAAW,EAAG,QAAO;AACzB,QAAI,WAAW,GAAI,QAAO,GAAG,QAAQ;AACrC,QAAI,YAAY,GAAI,QAAO,GAAG,SAAS;AACvC,QAAI,WAAW,EAAG,QAAO,GAAG,QAAQ;AACpC,WAAO,KAAK,mBAAmB;AAAA,EACjC,QAAQ;AAAE,WAAO;AAAA,EAAS;AAC5B;AAEA,IAAO,4BAAQ;AAAA,EACb,MAAM,CAAC,SAAgC;AACrC,UAAM,WAAW,KAAK,YAAY;AAClC,WAAO,kGAAkG,WAAW,QAAQ,CAAC;AAAA,EAC/H;AAAA,EACA,QAAQ,CAAC,MAAiI,YAAsB;AAC9J,QAAI,CAAC,QAAS,QAAO,YAAY,IAAI;AACrC,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,QAAI,UAAU,GAAG;AACf,aAAO;AAAA,IACT;AACA,WAAO,gHAAgH,KAAK,iiBAAiiB,MAAM,IAAI,OAAK,yDAAyD,EAAE,QAAQ,MAAM,IAAI,KAAK,oCAAoC,2DAA2D,WAAW,OAAO,EAAE,KAAK,CAAC,CAAC,qHAAqH,WAAW,EAAE,YAAY,CAAC,KAAK,WAAW,EAAE,YAAY,CAAC,gEAAgE,WAAW,eAAe,EAAE,YAAY,CAAC,CAAC,iFAAiF,WAAW,EAAE,kBAAkB,KAAK,CAAC,YAAY,EAAE,KAAK,EAAE,CAAC;AAAA,EAC9xC;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/templates/trash-restore.render.ts"],"sourcesContent":["/**\n * Safe Trash Restore 渲染模板\n */\n\nimport type { InlineRenderTemplate } from '@agentdevjs/core';\n\nfunction escapeHtml(text: unknown): string {\n const str = String(text);\n const map: Record<string, string> = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' };\n return str.replace(/[&<>\"']/g, m => map[m]!);\n}\n\nfunction formatError(data: unknown): string {\n const text = typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data);\n return `<div class=\"tool-error\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg><span>${escapeHtml(text)}</span></div>`;\n}\n\nexport default {\n call: (args: { target?: string | number }) => {\n const target = args.target !== undefined ? escapeHtml(String(args.target)) : '';\n return `<div class=\"bash-command\">Restore <span class=\"pattern\">${target}</span></div>`;\n },\n result: (data: { restored?: string[]; failed?: Array<{ path: string; error: string }> }, success?: boolean) => {\n if (!success) return formatError(data);\n const restored = data.restored || [];\n const failed = data.failed || [];\n if (restored.length > 0 && failed.length === 0) {\n let html = `<div style=\"color:var(--success-color)\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>Restored ${restored.length} item(s)</div>`;\n if (restored.length > 0 && restored.length <= 5) {\n html += `<div style=\"font-size:11px;color:var(--text-secondary);margin-top:4px\">${restored.map(p => escapeHtml(p)).join(', ')}</div>`;\n }\n return html;\n }\n if (restored.length > 0 || failed.length > 0) {\n let html = '';\n if (restored.length > 0) {\n html += `<div style=\"color:var(--success-color);margin-bottom:4px\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>Restored ${restored.length}</div>`;\n }\n if (failed.length > 0) {\n html += `<div style=\"color:var(--error-color);margin-bottom:4px\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>${failed.length} failed</div>`;\n const errorMessages = failed.map(f => `${escapeHtml(f.path)}: ${escapeHtml(f.error)}`).join('; ');\n html += `<div style=\"font-size:11px;color:var(--error-color);margin-top:4px\">${errorMessages}</div>`;\n }\n return html || `<div style=\"color:var(--text-secondary)\">Nothing restored</div>`;\n }\n return `<div style=\"color:var(--text-secondary)\">Nothing restored</div>`;\n }\n} as const satisfies InlineRenderTemplate;\n"],"mappings":";AAMA,SAAS,WAAW,MAAuB;AACzC,QAAM,MAAM,OAAO,IAAI;AACvB,QAAM,MAA8B,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ;AAC1G,SAAO,IAAI,QAAQ,YAAY,OAAK,IAAI,CAAC,CAAE;AAC7C;AAEA,SAAS,YAAY,MAAuB;AAC1C,QAAM,OAAO,OAAO,SAAS,WAAW,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,OAAO,IAAI;AACnF,SAAO,uNAAuN,WAAW,IAAI,CAAC;AAChP;AAEA,IAAO,+BAAQ;AAAA,EACb,MAAM,CAAC,SAAuC;AAC5C,UAAM,SAAS,KAAK,WAAW,SAAY,WAAW,OAAO,KAAK,MAAM,CAAC,IAAI;AAC7E,WAAO,2DAA2D,MAAM;AAAA,EAC1E;AAAA,EACA,QAAQ,CAAC,MAAgF,YAAsB;AAC7G,QAAI,CAAC,QAAS,QAAO,YAAY,IAAI;AACrC,UAAM,WAAW,KAAK,YAAY,CAAC;AACnC,UAAM,SAAS,KAAK,UAAU,CAAC;AAC/B,QAAI,SAAS,SAAS,KAAK,OAAO,WAAW,GAAG;AAC9C,UAAI,OAAO,0OAA0O,SAAS,MAAM;AACpQ,UAAI,SAAS,SAAS,KAAK,SAAS,UAAU,GAAG;AAC/C,gBAAQ,0EAA0E,SAAS,IAAI,OAAK,WAAW,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAC/H;AACA,aAAO;AAAA,IACT;AACA,QAAI,SAAS,SAAS,KAAK,OAAO,SAAS,GAAG;AAC5C,UAAI,OAAO;AACX,UAAI,SAAS,SAAS,GAAG;AACvB,gBAAQ,4PAA4P,SAAS,MAAM;AAAA,MACrR;AACA,UAAI,OAAO,SAAS,GAAG;AACrB,gBAAQ,gSAAgS,OAAO,MAAM;AACrT,cAAM,gBAAgB,OAAO,IAAI,OAAK,GAAG,WAAW,EAAE,IAAI,CAAC,KAAK,WAAW,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI;AAChG,gBAAQ,uEAAuE,aAAa;AAAA,MAC9F;AACA,aAAO,QAAQ;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/templates/trash-restore.render.ts"],"sourcesContent":["/**\r\n * Safe Trash Restore 渲染模板\r\n */\r\n\r\nimport type { InlineRenderTemplate } from '@agentdevjs/core';\r\n\r\nfunction escapeHtml(text: unknown): string {\r\n const str = String(text);\r\n const map: Record<string, string> = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' };\r\n return str.replace(/[&<>\"']/g, m => map[m]!);\r\n}\r\n\r\nfunction formatError(data: unknown): string {\r\n const text = typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data);\r\n return `<div class=\"tool-error\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg><span>${escapeHtml(text)}</span></div>`;\r\n}\r\n\r\nexport default {\r\n call: (args: { target?: string | number }) => {\r\n const target = args.target !== undefined ? escapeHtml(String(args.target)) : '';\r\n return `<div class=\"bash-command\">Restore <span class=\"pattern\">${target}</span></div>`;\r\n },\r\n result: (data: { restored?: string[]; failed?: Array<{ path: string; error: string }> }, success?: boolean) => {\r\n if (!success) return formatError(data);\r\n const restored = data.restored || [];\r\n const failed = data.failed || [];\r\n if (restored.length > 0 && failed.length === 0) {\r\n let html = `<div style=\"color:var(--success-color)\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>Restored ${restored.length} item(s)</div>`;\r\n if (restored.length > 0 && restored.length <= 5) {\r\n html += `<div style=\"font-size:11px;color:var(--text-secondary);margin-top:4px\">${restored.map(p => escapeHtml(p)).join(', ')}</div>`;\r\n }\r\n return html;\r\n }\r\n if (restored.length > 0 || failed.length > 0) {\r\n let html = '';\r\n if (restored.length > 0) {\r\n html += `<div style=\"color:var(--success-color);margin-bottom:4px\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/></svg>Restored ${restored.length}</div>`;\r\n }\r\n if (failed.length > 0) {\r\n html += `<div style=\"color:var(--error-color);margin-bottom:4px\"><svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" style=\"vertical-align:middle;margin-right:4px\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>${failed.length} failed</div>`;\r\n const errorMessages = failed.map(f => `${escapeHtml(f.path)}: ${escapeHtml(f.error)}`).join('; ');\r\n html += `<div style=\"font-size:11px;color:var(--error-color);margin-top:4px\">${errorMessages}</div>`;\r\n }\r\n return html || `<div style=\"color:var(--text-secondary)\">Nothing restored</div>`;\r\n }\r\n return `<div style=\"color:var(--text-secondary)\">Nothing restored</div>`;\r\n }\r\n} as const satisfies InlineRenderTemplate;\r\n"],"mappings":";AAMA,SAAS,WAAW,MAAuB;AACzC,QAAM,MAAM,OAAO,IAAI;AACvB,QAAM,MAA8B,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ;AAC1G,SAAO,IAAI,QAAQ,YAAY,OAAK,IAAI,CAAC,CAAE;AAC7C;AAEA,SAAS,YAAY,MAAuB;AAC1C,QAAM,OAAO,OAAO,SAAS,WAAW,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,OAAO,IAAI;AACnF,SAAO,uNAAuN,WAAW,IAAI,CAAC;AAChP;AAEA,IAAO,+BAAQ;AAAA,EACb,MAAM,CAAC,SAAuC;AAC5C,UAAM,SAAS,KAAK,WAAW,SAAY,WAAW,OAAO,KAAK,MAAM,CAAC,IAAI;AAC7E,WAAO,2DAA2D,MAAM;AAAA,EAC1E;AAAA,EACA,QAAQ,CAAC,MAAgF,YAAsB;AAC7G,QAAI,CAAC,QAAS,QAAO,YAAY,IAAI;AACrC,UAAM,WAAW,KAAK,YAAY,CAAC;AACnC,UAAM,SAAS,KAAK,UAAU,CAAC;AAC/B,QAAI,SAAS,SAAS,KAAK,OAAO,WAAW,GAAG;AAC9C,UAAI,OAAO,0OAA0O,SAAS,MAAM;AACpQ,UAAI,SAAS,SAAS,KAAK,SAAS,UAAU,GAAG;AAC/C,gBAAQ,0EAA0E,SAAS,IAAI,OAAK,WAAW,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAC/H;AACA,aAAO;AAAA,IACT;AACA,QAAI,SAAS,SAAS,KAAK,OAAO,SAAS,GAAG;AAC5C,UAAI,OAAO;AACX,UAAI,SAAS,SAAS,GAAG;AACvB,gBAAQ,4PAA4P,SAAS,MAAM;AAAA,MACrR;AACA,UAAI,OAAO,SAAS,GAAG;AACrB,gBAAQ,gSAAgS,OAAO,MAAM;AACrT,cAAM,gBAAgB,OAAO,IAAI,OAAK,GAAG,WAAW,EAAE,IAAI,CAAC,KAAK,WAAW,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI;AAChG,gBAAQ,uEAAuE,aAAa;AAAA,MAC9F;AACA,aAAO,QAAQ;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
package/package.json CHANGED
@@ -1,47 +1,47 @@
1
- {
2
- "name": "@agentdevjs/shell-feature",
3
- "version": "0.1.0",
4
- "description": "Shell execution feature for AgentDev - provides bash execution and safe trash operations",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "files": [
9
- "dist",
10
- "README.md"
11
- ],
12
- "scripts": {
13
- "build": "tsup",
14
- "dev": "tsup --watch",
15
- "test": "vitest run",
16
- "test:watch": "vitest",
17
- "prepublishOnly": "npm run build"
18
- },
19
- "tsup": {
20
- "entry": [
21
- "src/index.ts",
22
- "src/templates/*.render.ts"
23
- ],
24
- "format": "esm",
25
- "dts": true,
26
- "clean": true,
27
- "sourcemap": true
28
- },
29
- "peerDependencies": {
30
- "@agentdevjs/core": "^0.1.0"
31
- },
32
- "devDependencies": {
33
- "@types/node": "^20.11.0",
34
- "tsup": "^8.3.5",
35
- "typescript": "^5.3.3",
36
- "vitest": "^4.1.10",
37
- "@agentdevjs/core": "file:../core"
38
- },
39
- "keywords": [
40
- "agentdev",
41
- "feature",
42
- "shell",
43
- "bash",
44
- "trash"
45
- ],
46
- "license": "MIT"
47
- }
1
+ {
2
+ "name": "@agentdevjs/shell-feature",
3
+ "version": "0.1.1",
4
+ "description": "Shell execution feature for AgentDev - provides bash execution and safe trash operations",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "dev": "tsup --watch",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "tsup": {
20
+ "entry": [
21
+ "src/index.ts",
22
+ "src/templates/*.render.ts"
23
+ ],
24
+ "format": "esm",
25
+ "dts": true,
26
+ "clean": true,
27
+ "sourcemap": true
28
+ },
29
+ "peerDependencies": {
30
+ "@agentdevjs/core": "^0.1.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^20.11.0",
34
+ "tsup": "^8.3.5",
35
+ "typescript": "^5.3.3",
36
+ "vitest": "^4.1.10",
37
+ "@agentdevjs/core": "file:../core"
38
+ },
39
+ "keywords": [
40
+ "agentdev",
41
+ "feature",
42
+ "shell",
43
+ "bash",
44
+ "trash"
45
+ ],
46
+ "license": "MIT"
47
+ }