/** * Mock runtime — Sample.mocks 落地的核心逻辑。 * * 两条路径: * 1) `buildSdkHookCallback(mocks, baseDir)` * → 给 claude-sdk executor 用,返回 in-process PreToolUse HookCallback。 * 纯内存,零 IO,零 spawn 开销。 * * 2) `materializeForCliConfigDir(mocks, baseDir)` * → 给 claude-cli executor 用,mktemp 一个临时 CLAUDE_CONFIG_DIR, * 在里面写 settings.json + on-disk hook 脚本 + mocks.json,返回 cleanup。 * claude 子进程退出后(成功/失败/超时/SIGINT),cleanup 必删整个临时目录。 * * 共用核心:`matchMock(mocks, toolName, toolInput, callCounter)` 命中规则评估。 */ import type { Mock } from '../types/eval.js'; /** 单条 mock 是否命中给定 tool 调用。 */ export declare function isMockHit(mock: Mock, toolName: string, toolInput: unknown): boolean; /** 把 mock 的 return / return_file / return_seq 解析成可序列化字符串(LLM 看到的 tool_result)。 */ export declare function resolveMockReturn(mock: Mock, hitCount: number, baseDir?: string): string; /** SDK PreToolUse hook input(精简版,只用我们关心的字段)。 */ interface SdkHookInput { hook_event_name: 'PreToolUse'; tool_name: string; tool_input: unknown; } /** SDK PreToolUse hook output:`block` 决策 + permissionDecisionReason 当 mock content。 */ interface SdkHookOutput { continue?: boolean; decision?: 'block' | 'approve'; hookSpecificOutput?: { hookEventName: 'PreToolUse'; permissionDecision?: 'deny' | 'allow' | 'ask'; permissionDecisionReason?: string; }; systemMessage?: string; } export interface SdkHookHandle { /** 给 SDK options.hooks.PreToolUse 用的 callback。 */ callback: (input: SdkHookInput) => Promise; /** 拦截命中统计(诊断用)。 */ stats: { hits: number; misses: number; perMock: Record; }; } /** * 把 mocks 转成 SDK in-process PreToolUse hook callback。 * * 命中策略:strict 模式下不命中的工具调用直接 deny(防意外真调)。 * 默认非 strict:不命中放行(原生工具继续真跑)— 适合"部分 mock,部分真跑"场景。 * * @param mocks Sample.mocks 数组 * @param baseDir 解析 mock.return_file 的相对路径锚点 * @param strict true → 不命中 deny;false → 不命中放行(default) */ export declare function buildSdkHookCallback(mocks: Mock[] | undefined, baseDir?: string, strict?: boolean): SdkHookHandle; export interface CliMockHandle { /** 临时 settings 文件路径,作为 `claude --settings ` 参数传入。 * Claude Code 会**追加**这个 settings 到 ~/.claude/settings.json,**不替换** — * 这样 OAuth 登录态 / 用户主配置都不动。 */ settingsFile: string; /** 临时 MCP 配置文件路径,作为 `claude --mcp-config --strict-mcp-config` 参数传入。 */ mcpConfigFile?: string; /** 子进程要看到的额外 env(目前只 OMK_MOCKS_FILE)。 */ env: Record; /** 读 hook 在临时目录写的命中统计(给 ExecResult.mockStats 用)。 * 必须在 cleanup 之前调,cleanup 后文件就没了。 */ readStats: () => { hits: number; misses: number; perMock: Record; }; /** 必须在 spawn 完成后(无论 success/error/timeout)调用。 */ cleanup: () => void; } /** * 物化 mocks 到临时 settings 文件 + on-disk hook 脚本。 * * **关键**:用 Claude Code 的 `--settings ` flag 而非 `CLAUDE_CONFIG_DIR`。 * `--settings` 是**追加**模式,主 `~/.claude/` 包含的 OAuth 登录态 / 用户配置全部保留; * 而 CLAUDE_CONFIG_DIR 是**替换**模式,设它会丢 OAuth → "Not logged in"。 * * 临时目录结构: * $tmpdir/omk-mocks-XXXXXX/ * ├── settings.json (PreToolUse hook 注册,作为 --settings 参数) * ├── mock-hook.cjs (hook 实现:读 OMK_MOCKS_FILE → 匹配 → 输出 JSON 决策) * ├── fake-mcp-server.cjs (可选:给 mcp__server__tool mock 注册一次性 fake MCP) * ├── mcp.json (可选:作为 --mcp-config 参数) * └── mocks.json (mocks 序列化 + baseDir + strict) * * cleanup 必删整个目录。executor 用 try / finally 保证执行。 */ export declare function materializeForCliConfigDir(mocks: Mock[] | undefined, baseDir?: string, strict?: boolean): CliMockHandle | null; declare function readMockHookTemplate(): string; export declare const _readMockHookTemplateForTest: typeof readMockHookTemplate; export declare function _testMakeTempConfigDir(): string; export {};