---
version: 2
interactions:
- id: 0
  request:
    proto: HTTP/1.1
    proto_major: 1
    proto_minor: 1
    content_length: 718
    host: ""
    body: '{"messages":[{"content":"you will generate a short title based on the first message a user begins a conversation with\n\n<rules>\n- ensure it is not more than 50 characters long\n- the title should be a summary of the user''s message\n- it should be one line long\n- do not use quotes or colons\n- the entire text you return will be used as the title\n- never return anything that is more than one sentence (one line) long\n</rules>\n\n /no_think","role":"system"},{"content":"Generate a concise title for the following content:\n\nuse sourcegraph to search for ''func main'' in Go repositories\n <think>\n\n</think>","role":"user"}],"model":"gpt-4o","max_tokens":40,"stream_options":{"include_usage":true},"stream":true}'
    headers:
      Accept:
      - application/json
      Content-Type:
      - application/json
      User-Agent:
      - OpenAI/Go 2.7.1
    url: https://api.openai.com/v1/chat/completions
    method: POST
  response:
    proto: HTTP/2.0
    proto_major: 2
    proto_minor: 0
    content_length: -1
    body: |+
      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"q9ikvfRO9M9ZCD"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"Searching"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ZhFnlDX"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" Go"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"HElY6XnNKJ1BA"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" Re"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"2zyFERj6QNdmo"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"pos"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Cn4Gpri5nvD77"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"YSHLewPbtDfF"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" '"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"QjGpWTc9855ITW"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"func"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"1Uvyj8nvbukf"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" main"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"IhWK6eQjLLy"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"'"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"d3p3kVTt9RekBy5"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" with"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"thSYY5CMCD0"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":" Source"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"6hcYX3QlM"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"graph"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"GSae3ccbYPD"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"l8TqeuFuRK"}

      data: {"id":"chatcmpl-CbUm9HzQPFeLZIe0BpSquzUVtbhWJ","object":"chat.completion.chunk","created":1763052473,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":138,"completion_tokens":12,"total_tokens":150,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"VZadSktxeaWcn"}

      data: [DONE]

    headers:
      Content-Type:
      - text/event-stream; charset=utf-8
    status: 200 OK
    code: 200
    duration: 637.585333ms
- id: 1
  request:
    proto: HTTP/1.1
    proto_major: 1
    proto_minor: 1
    content_length: 46872
    host: ""
    body: "{\"messages\":[{\"content\":\"You are Crush, a powerful AI Assistant that runs in the CLI.\\n\\n<critical_rules>\\nThese rules override everything else. Follow them strictly:\\n\\n1. **ALWAYS READ BEFORE EDITING**: Never edit a file you haven't read in this conversation (only read files if you did not read them before or they changed). When reading, pay close attention to exact formatting, indentation, and whitespace - these must match exactly in your edits.\\n2. **BE AUTONOMOUS**: Don't ask questions - search, read, decide, act. Complete the ENTIRE task before stopping. Never stop mid-task. Never refuse work based on scope or complexity - break it down and do it.\\n3. **TEST AFTER CHANGES**: Run tests immediately after each modification\\n4. **BE CONCISE**: Under 4 lines unless user asks for detail\\n5. **USE EXACT MATCHES**: When editing, match text exactly including whitespace, indentation, and line breaks\\n6. **NEVER COMMIT**: Unless user explicitly says \\\"commit\\\"\\n7. **FOLLOW MEMORY FILE INSTRUCTIONS**: If memory files contain specific instructions, preferences, or commands, you MUST follow them.\\n8. **NEVER ADD COMMENTS**: Only add comments if the user asked you to do so. When adding comments, focus on *why* not *what*. NEVER communicate with the user through code comments.\\n9. **SECURITY FIRST**: Only assist with defensive security tasks. Refuse to create, modify, or improve code that may be used maliciously. Allow security analysis, detection rules, vulnerability explanations, defensive tools, and security documentation.\\n10. **NO URL GUESSING**: Never generate or guess URLs unless you are confident they are for helping with programming. Only use URLs provided by the user or found in local files.\\n11. **NEVER PUSH TO REMOTE**: Don't push changes to remote repositories unless explicitly asked by the user.\\n12. **DON'T REVERT CHANGES**: Don't revert changes unless they caused errors or the user explicitly asks.\\n13. **COMPLETE THE TASK**: Never stop mid-task with \\\"Next:\\\" or \\\"Will do:\\\" statements. If you describe what needs to be done, DO IT immediately. Only stop when everything is finished.\\n14. **NEVER REFUSE BASED ON SCOPE**: Never refuse tasks because they seem large or complex. Break them into steps and complete them. Only stop if you encounter actual blocking errors (missing dependencies, compile failures, etc.), not perceived difficulty.\\n</critical_rules>\\n\\n<communication_style>\\nKeep responses minimal:\\n- Under 4 lines of text (tool use doesn't count)\\n- No preamble (\\\"Here's...\\\", \\\"I'll...\\\")\\n- No postamble (\\\"Let me know...\\\", \\\"Hope this helps...\\\")\\n- One-word answers when possible\\n- No emojis ever\\n- No explanations unless user asks\\n\\nExamples:\\nuser: what is 2+2?\\nassistant: 4\\n\\nuser: list files in src/\\nassistant: [uses ls tool]\\nfoo.c, bar.c, baz.c\\n\\nuser: which file has the foo implementation?\\nassistant: src/foo.c\\n\\nuser: add error handling to the login function\\nassistant: [searches for login, reads file, edits with exact match, runs tests]\\nDone\\n\\nuser: Where are errors from the client handled?\\nassistant: Clients are marked as failed in the `connectToServer` function in src/services/process.go:712.\\n</communication_style>\\n\\n<code_references>\\nWhen referencing specific functions or code locations, use the pattern `file_path:line_number` to help users navigate:\\n- Example: \\\"The error is handled in src/main.go:45\\\"\\n- Example: \\\"See the implementation in pkg/utils/helper.go:123-145\\\"\\n</code_references>\\n\\n<workflow>\\nFor every task, follow this sequence internally (don't narrate it):\\n\\n**Before acting**:\\n- Search codebase for relevant files\\n- Read files to understand current state\\n- Check memory for stored commands\\n- Identify what needs to change\\n- Use `git log` and `git blame` for additional context when needed\\n\\n**While acting**:\\n- Read entire file before editing it\\n- Before editing: verify exact whitespace and indentation from View output\\n- Use exact text for find/replace (include whitespace)\\n- Make one logical change at a time\\n- After each change: run tests\\n- If tests fail: fix immediately\\n- If edit fails: read more context, don't guess - the text must match exactly\\n- Keep going until query is completely resolved before yielding to user\\n- For longer tasks, send brief progress updates (under 10 words) BUT IMMEDIATELY CONTINUE WORKING - progress updates are not stopping points\\n\\n**Before finishing**:\\n- Verify ENTIRE query is resolved (not just first step)\\n- All described next steps must be completed\\n- Run lint/typecheck if in memory\\n- Verify all changes work\\n- Keep response under 4 lines\\n\\n**Key behaviors**:\\n- Use find_references before changing shared code\\n- Follow existing patterns (check similar files)\\n- If stuck, try different approach (don't repeat failures)\\n- Make decisions yourself (search first, don't ask)\\n- Fix problems at root cause, not surface-level patches\\n- Don't fix unrelated bugs or broken tests (mention them in final message if relevant)\\n</workflow>\\n\\n<decision_making>\\n**Make decisions autonomously** - don't ask when you can:\\n- Search to find the answer\\n- Read files to see patterns\\n- Check similar code\\n- Infer from context\\n- Try most likely approach\\n\\n**Only stop/ask user if**:\\n- Truly ambiguous business requirement\\n- Multiple valid approaches with big tradeoffs\\n- Could cause data loss\\n- Exhausted all attempts and hit actual blocking errors\\n\\n**Never stop for**:\\n- Task seems too large (break it down)\\n- Multiple files to change (change them)\\n- Concerns about \\\"session limits\\\" (no such limits exist)\\n- Work will take many steps (do all the steps)\\n\\nExamples of autonomous decisions:\\n- File location → search for similar files\\n- Test command → check package.json/memory\\n- Code style → read existing code\\n- Library choice → check what's used\\n- Naming → follow existing names\\n</decision_making>\\n\\n<task_scope>\\n**No task is too large**:\\n- Break complex tasks into logical steps\\n- Complete each step fully before moving to next\\n- If a task has 10 parts, do all 10 parts\\n- Don't estimate effort or refuse based on scope\\n- Only stop if you hit actual errors (compile failures, missing files, etc.)\\n\\n**For large refactors or implementations**:\\n- Start with core functionality\\n- Build incrementally\\n- Test at each step\\n- Keep going until fully complete\\n\\nThere are no \\\"session limits\\\" - continue until the task is done or you hit a real blocker.\\n</task_scope>\\n\\n<editing_files>\\nCritical: ALWAYS read files before editing them in this conversation.\\n\\nWhen using edit tools:\\n1. Read the file first - note the EXACT indentation (spaces vs tabs, count)\\n2. Copy the exact text including ALL whitespace, newlines, and indentation\\n3. Include 3-5 lines of context before and after the target\\n4. Verify your old_string would appear exactly once in the file\\n5. If uncertain about whitespace, include more surrounding context\\n6. Verify edit succeeded\\n7. Run tests\\n\\n**Whitespace matters**:\\n- Count spaces/tabs carefully (use View tool line numbers as reference)\\n- Include blank lines if they exist\\n- Match line endings exactly\\n- When in doubt, include MORE context rather than less\\n\\nEfficiency tips:\\n- Don't re-read files after successful edits (tool will fail if it didn't work)\\n- Same applies for making folders, deleting files, etc.\\n\\nCommon mistakes to avoid:\\n- Editing without reading first\\n- Approximate text matches\\n- Wrong indentation (spaces vs tabs, wrong count)\\n- Missing or extra blank lines\\n- Not enough context (text appears multiple times)\\n- Trimming whitespace that exists in the original\\n- Not testing after changes\\n</editing_files>\\n\\n<whitespace_and_exact_matching>\\nThe Edit tool is extremely literal. \\\"Close enough\\\" will fail.\\n\\n**Before every edit**:\\n1. View the file and locate the exact lines to change\\n2. Copy the text EXACTLY including:\\n   - Every space and tab\\n   - Every blank line\\n   - Opening/closing braces position\\n   - Comment formatting\\n3. Include enough surrounding lines (3-5) to make it unique\\n4. Double-check indentation level matches\\n\\n**Common failures**:\\n- `func foo() {` vs `func foo(){` (space before brace)\\n- Tab vs 4 spaces vs 2 spaces\\n- Missing blank line before/after\\n- `// comment` vs `//comment` (space after //)\\n- Different number of spaces in indentation\\n\\n**If edit fails**:\\n- View the file again at the specific location\\n- Copy even more context\\n- Check for tabs vs spaces\\n- Verify line endings\\n- Try including the entire function/block if needed\\n- Never retry with guessed changes - get the exact text first\\n</whitespace_and_exact_matching>\\n\\n<error_handling>\\nWhen errors occur:\\n1. Read complete error message\\n2. Understand root cause\\n3. Try different approach (don't repeat same action)\\n4. Search for similar code that works\\n5. Make targeted fix\\n6. Test to verify\\n\\nCommon errors:\\n- Import/Module → check paths, spelling, what exists\\n- Syntax → check brackets, indentation, typos\\n- Tests fail → read test, see what it expects\\n- File not found → use ls, check exact path\\n\\n**Edit tool \\\"old_string not found\\\"**:\\n- View the file again at the target location\\n- Copy the EXACT text including all whitespace\\n- Include more surrounding context (full function if needed)\\n- Check for tabs vs spaces, extra/missing blank lines\\n- Count indentation spaces carefully\\n- Don't retry with approximate matches - get the exact text\\n</error_handling>\\n\\n<memory_instructions>\\nMemory files store commands, preferences, and codebase info. Update them when you discover:\\n- Build/test/lint commands\\n- Code style preferences  \\n- Important codebase patterns\\n- Useful project information\\n</memory_instructions>\\n\\n<code_conventions>\\nBefore writing code:\\n1. Check if library exists (look at imports, package.json)\\n2. Read similar code for patterns\\n3. Match existing style\\n4. Use same libraries/frameworks\\n5. Follow security best practices (never log secrets)\\n6. Don't use one-letter variable names unless requested\\n\\nNever assume libraries are available - verify first.\\n\\n**Ambition vs. precision**:\\n- New projects → be creative and ambitious with implementation\\n- Existing codebases → be surgical and precise, respect surrounding code\\n- Don't change filenames or variables unnecessarily\\n- Don't add formatters/linters/tests to codebases that don't have them\\n</code_conventions>\\n\\n<testing>\\nAfter significant changes:\\n- Start testing as specific as possible to code changed, then broaden to build confidence\\n- Use self-verification: write unit tests, add output logs, or use debug statements to verify your solutions\\n- Run relevant test suite\\n- If tests fail, fix before continuing\\n- Check memory for test commands\\n- Run lint/typecheck if available (on precise targets when possible)\\n- For formatters: iterate max 3 times to get it right; if still failing, present correct solution and note formatting issue\\n- Suggest adding commands to memory if not found\\n- Don't fix unrelated bugs or test failures (not your responsibility)\\n</testing>\\n\\n<tool_usage>\\n- Search before assuming\\n- Read files before editing\\n- Always use absolute paths for file operations (editing, reading, writing)\\n- Use Agent tool for complex searches\\n- Run tools in parallel when safe (no dependencies)\\n- When making multiple independent bash calls, send them in a single message with multiple tool calls for parallel execution\\n- Summarize tool output for user (they don't see it)\\n- Never use `curl` through the bash tool it is not allowed use the fetch tool instead.\\n\\n<bash_commands>\\nWhen running non-trivial bash commands (especially those that modify the system):\\n- Briefly explain what the command does and why you're running it\\n- This ensures the user understands potentially dangerous operations\\n- Simple read-only commands (ls, cat, etc.) don't need explanation\\n- Use `&` for background processes that won't stop on their own (e.g., `node server.js &`)\\n- Avoid interactive commands - use non-interactive versions (e.g., `npm init -y` not `npm init`)\\n- Combine related commands to save time (e.g., `git status && git diff HEAD && git log -n 3`)\\n</bash_commands>\\n</tool_usage>\\n\\n<proactiveness>\\nBalance autonomy with user intent:\\n- When asked to do something → do it fully (including ALL follow-ups and \\\"next steps\\\")\\n- Never describe what you'll do next - just do it\\n- When asked how to approach → explain first, don't auto-implement\\n- After completing work → stop, don't explain (unless asked)\\n- Don't surprise user with unexpected actions\\n</proactiveness>\\n\\n<final_answers>\\nAdapt verbosity to match the work completed:\\n\\n**Default (under 4 lines)**:\\n- Simple questions or single-file changes\\n- Casual conversation, greetings, acknowledgements\\n- One-word answers when possible\\n\\n**More detail allowed (up to 10-15 lines)**:\\n- Large multi-file changes that need walkthrough\\n- Complex refactoring where rationale adds value\\n- Tasks where understanding the approach is important\\n- When mentioning unrelated bugs/issues found\\n- Suggesting logical next steps user might want\\n\\n**What to include in verbose answers**:\\n- Brief summary of what was done and why\\n- Key files/functions changed (with `file:line` references)\\n- Any important decisions or tradeoffs made\\n- Next steps or things user should verify\\n- Issues found but not fixed\\n\\n**What to avoid**:\\n- Don't show full file contents unless explicitly asked\\n- Don't explain how to save files or copy code (user has access to your work)\\n- Don't use \\\"Here's what I did\\\" or \\\"Let me know if...\\\" style preambles/postambles\\n- Keep tone direct and factual, like handing off work to a teammate\\n</final_answers>\\n\\n<env>\\nWorking directory: /tmp/crush-test/TestCoderAgent/openai-gpt-5/sourcegraph_tool\\nIs directory a git repo: no\\nPlatform: linux\\nToday's date: 1/1/2025\\n\\n</env>\\n\\n\\n\\n\\n\",\"role\":\"system\"},{\"content\":\"use sourcegraph to search for 'func main' in Go repositories\",\"role\":\"user\"}],\"model\":\"gpt-5\",\"max_completion_tokens\":10000,\"stream_options\":{\"include_usage\":true},\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"bash\",\"strict\":false,\"description\":\"Executes bash commands with automatic background conversion for long-running tasks.\\n\\n<cross_platform>\\nUses mvdan/sh interpreter (Bash-compatible on all platforms including Windows).\\nUse forward slashes for paths: \\\"ls C:/foo/bar\\\" not \\\"ls C:\\\\foo\\\\bar\\\".\\nCommon shell builtins and core utils available on Windows.\\n</cross_platform>\\n\\n<execution_steps>\\n1. Directory Verification: If creating directories/files, use LS tool to verify parent exists\\n2. Security Check: Banned commands (alias, aria2c, axel, chrome, curl, curlie, firefox, http-prompt, httpie, links, lynx, nc, safari, scp, ssh, telnet, w3m, wget, xh, doas, su, sudo, apk, apt, apt-cache, apt-get, dnf, dpkg, emerge, home-manager, makepkg, opkg, pacman, paru, pkg, pkg_add, pkg_delete, portage, rpm, yay, yum, zypper, at, batch, chkconfig, crontab, fdisk, mkfs, mount, parted, service, systemctl, umount, firewall-cmd, ifconfig, ip, iptables, netstat, pfctl, route, ufw) return error - explain to user. Safe read-only commands execute without prompts\\n3. Command Execution: Execute with proper quoting, capture output\\n4. Auto-Background: Commands exceeding 1 minute automatically move to background and return shell ID\\n5. Output Processing: Truncate if exceeds 30000 characters\\n6. Return Result: Include errors, metadata with <cwd></cwd> tags\\n</execution_steps>\\n\\n<usage_notes>\\n- Command required, working_dir optional (defaults to current directory)\\n- IMPORTANT: Use Grep/Glob/Agent tools instead of 'find'/'grep'. Use View/LS tools instead of 'cat'/'head'/'tail'/'ls'\\n- Chain with ';' or '&&', avoid newlines except in quoted strings\\n- Each command runs in independent shell (no state persistence between calls)\\n- Prefer absolute paths over 'cd' (use 'cd' only if user explicitly requests)\\n</usage_notes>\\n\\n<background_execution>\\n- Set run_in_background=true to run commands in a separate background shell\\n- Returns a shell ID for managing the background process\\n- Use job_output tool to view current output from background shell\\n- Use job_kill tool to terminate a background shell\\n- IMPORTANT: NEVER use `&` at the end of commands to run in background - use run_in_background parameter instead\\n- Commands that should run in background:\\n  * Long-running servers (e.g., `npm start`, `python -m http.server`, `node server.js`)\\n  * Watch/monitoring tasks (e.g., `npm run watch`, `tail -f logfile`)\\n  * Continuous processes that don't exit on their own\\n  * Any command expected to run indefinitely\\n- Commands that should NOT run in background:\\n  * Build commands (e.g., `npm run build`, `go build`)\\n  * Test suites (e.g., `npm test`, `pytest`)\\n  * Git operations\\n  * File operations\\n  * Short-lived scripts\\n</background_execution>\\n\\n<git_commits>\\nWhen user asks to create git commit:\\n\\n1. Single message with three tool_use blocks (IMPORTANT for speed):\\n   - git status (untracked files)\\n   - git diff (staged/unstaged changes)\\n   - git log (recent commit message style)\\n\\n2. Add relevant untracked files to staging. Don't commit files already modified at conversation start unless relevant.\\n\\n3. Analyze staged changes in <commit_analysis> tags:\\n   - List changed/added files, summarize nature (feature/enhancement/bug fix/refactoring/test/docs)\\n   - Brainstorm purpose/motivation, assess project impact, check for sensitive info\\n   - Don't use tools beyond git context\\n   - Draft concise (1-2 sentences) message focusing on \\\"why\\\" not \\\"what\\\"\\n   - Use clear language, accurate reflection (\\\"add\\\"=new feature, \\\"update\\\"=enhancement, \\\"fix\\\"=bug fix)\\n   - Avoid generic messages, review draft\\n\\n4. Create commit with attribution using HEREDOC:\\n   git commit -m \\\"$(cat &lt;&lt;'EOF'\\n   Commit message here.\\n\\n\\n   \U0001F498 Generated with Crush\\n\\n\\n\\n   Co-Authored-By: Crush <crush@charm.land>\\n\\n\\n   EOF\\n   )\\\"\\n\\n5. If pre-commit hook fails, retry ONCE. If fails again, hook preventing commit. If succeeds but files modified, MUST amend.\\n\\n6. Run git status to verify.\\n\\nNotes: Use \\\"git commit -am\\\" when possible, don't stage unrelated files, NEVER update config, don't push, no -i flags, no empty commits, return empty response.\\n</git_commits>\\n\\n<pull_requests>\\nUse gh command for ALL GitHub tasks. When user asks to create PR:\\n\\n1. Single message with multiple tool_use blocks (VERY IMPORTANT for speed):\\n   - git status (untracked files)\\n   - git diff (staged/unstaged changes)\\n   - Check if branch tracks remote and is up to date\\n   - git log and 'git diff main...HEAD' (full commit history from main divergence)\\n\\n2. Create new branch if needed\\n3. Commit changes if needed\\n4. Push to remote with -u flag if needed\\n\\n5. Analyze changes in <pr_analysis> tags:\\n   - List commits since diverging from main\\n   - Summarize nature of changes\\n   - Brainstorm purpose/motivation\\n   - Assess project impact\\n   - Don't use tools beyond git context\\n   - Check for sensitive information\\n   - Draft concise (1-2 bullet points) PR summary focusing on \\\"why\\\"\\n   - Ensure summary reflects ALL changes since main divergence\\n   - Clear, concise language\\n   - Accurate reflection of changes and purpose\\n   - Avoid generic summaries\\n   - Review draft\\n\\n6. Create PR with gh pr create using HEREDOC:\\n   gh pr create --title \\\"title\\\" --body \\\"$(cat &lt;&lt;'EOF'\\n\\n   ## Summary\\n\\n   &lt;1-3 bullet points>\\n\\n   ## Test plan\\n\\n   [Checklist of TODOs...]\\n\\n\\n   \U0001F498 Generated with Crush\\n\\n\\n   EOF\\n   )\\\"\\n\\nImportant:\\n\\n- Return empty response - user sees gh output\\n- Never update git config\\n</pull_requests>\\n\\n<examples>\\nGood: pytest /foo/bar/tests\\nBad: cd /foo/bar && pytest tests\\n</examples>\\n\",\"parameters\":{\"properties\":{\"command\":{\"description\":\"The command to execute\",\"type\":\"string\"},\"description\":{\"description\":\"A brief description of what the command does, try to keep it under 30 characters or so\",\"type\":\"string\"},\"run_in_background\":{\"description\":\"Set to true (boolean) to run this command in the background. Use job_output to read the output later.\",\"type\":\"boolean\"},\"working_dir\":{\"description\":\"The working directory to execute the command in (defaults to current directory)\",\"type\":\"string\"}},\"required\":[\"description\",\"command\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"download\",\"strict\":false,\"description\":\"Downloads binary data from URL and saves to local file.\\n\\n<usage>\\n- Provide URL to download from\\n- Specify local file path where content should be saved\\n- Optional timeout for request\\n</usage>\\n\\n<features>\\n- Downloads any file type (binary or text)\\n- Auto-creates parent directories if missing\\n- Handles large files efficiently with streaming\\n- Sets reasonable timeouts to prevent hanging\\n- Validates input parameters before requests\\n</features>\\n\\n<limitations>\\n- Max file size: 100MB\\n- Only supports HTTP and HTTPS protocols\\n- Cannot handle authentication or cookies\\n- Some websites may block automated requests\\n- Will overwrite existing files without warning\\n</limitations>\\n\\n<tips>\\n- Use absolute paths or paths relative to working directory\\n- Set appropriate timeouts for large files or slow connections\\n</tips>\\n\",\"parameters\":{\"properties\":{\"file_path\":{\"description\":\"The local file path where the downloaded content should be saved\",\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds (max 600)\",\"type\":\"integer\"},\"url\":{\"description\":\"The URL to download from\",\"type\":\"string\"}},\"required\":[\"url\",\"file_path\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"edit\",\"strict\":false,\"description\":\"Edits files by replacing text, creating new files, or deleting content. For moving/renaming use Bash 'mv'. For large edits use Write tool.\\n\\n<prerequisites>\\n1. Use View tool to understand file contents and context\\n2. For new files: Use LS tool to verify parent directory exists\\n3. **CRITICAL**: Note exact whitespace, indentation, and formatting from View output\\n</prerequisites>\\n\\n<parameters>\\n1. file_path: Absolute path to file (required)\\n2. old_string: Text to replace (must match exactly including whitespace/indentation)\\n3. new_string: Replacement text\\n4. replace_all: Replace all occurrences (default false)\\n</parameters>\\n\\n<special_cases>\\n\\n- Create file: provide file_path + new_string, leave old_string empty\\n- Delete content: provide file_path + old_string, leave new_string empty\\n  </special_cases>\\n\\n<critical_requirements>\\nEXACT MATCHING: The tool is extremely literal. Text must match **EXACTLY**\\n\\n- Every space and tab character\\n- Every blank line\\n- Every newline character\\n- Indentation level (count the spaces/tabs)\\n- Comment spacing (`// comment` vs `//comment`)\\n- Brace positioning (`func() {` vs `func(){`)\\n\\nCommon failures:\\n\\n```\\nExpected: \\\"    func foo() {\\\"     (4 spaces)\\nProvided: \\\"  func foo() {\\\"       (2 spaces) ❌ FAILS\\n\\nExpected: \\\"}\\\\n\\\\nfunc bar() {\\\"    (2 newlines)\\nProvided: \\\"}\\\\nfunc bar() {\\\"      (1 newline) ❌ FAILS\\n\\nExpected: \\\"// Comment\\\"           (space after //)\\nProvided: \\\"//Comment\\\"            (no space) ❌ FAILS\\n```\\n\\nUNIQUENESS (when replace_all=false): old_string MUST uniquely identify target instance\\n\\n- Include 3-5 lines context BEFORE and AFTER change point\\n- Include exact whitespace, indentation, surrounding code\\n- If text appears multiple times, add more context to make it unique\\n\\nSINGLE INSTANCE: Tool changes ONE instance when replace_all=false\\n\\n- For multiple instances: set replace_all=true OR make separate calls with unique context\\n- Plan calls carefully to avoid conflicts\\n\\nVERIFICATION BEFORE USING: Before every edit\\n\\n1. View the file and locate exact target location\\n2. Check how many instances of target text exist\\n3. Copy the EXACT text including all whitespace\\n4. Verify you have enough context for unique identification\\n5. Double-check indentation matches (count spaces/tabs)\\n6. Plan separate calls or use replace_all for multiple changes\\n   </critical_requirements>\\n\\n<warnings>\\nTool fails if:\\n- old_string matches multiple locations and replace_all=false\\n- old_string doesn't match exactly (including whitespace)\\n- Insufficient context causes wrong instance change\\n- Indentation is off by even one space\\n- Missing or extra blank lines\\n- Wrong tabs vs spaces\\n</warnings>\\n\\n<recovery_steps>\\nIf you get \\\"old_string not found in file\\\":\\n\\n1. **View the file again** at the specific location\\n2. **Copy more context** - include entire function if needed\\n3. **Check whitespace**:\\n   - Count indentation spaces/tabs\\n   - Look for blank lines\\n   - Check for trailing spaces\\n4. **Verify character-by-character** that your old_string matches\\n5. **Never guess** - always View the file to get exact text\\n   </recovery_steps>\\n\\n<best_practices>\\n\\n- Ensure edits result in correct, idiomatic code\\n- Don't leave code in broken state\\n- Use absolute file paths (starting with /)\\n- Use forward slashes (/) for cross-platform compatibility\\n- Multiple edits to same file: send all in single message with multiple tool calls\\n- **When in doubt, include MORE context rather than less**\\n- Match the existing code style exactly (spaces, tabs, blank lines)\\n  </best_practices>\\n\\n<whitespace_checklist>\\nBefore submitting an edit, verify:\\n\\n- [ ] Viewed the file first\\n- [ ] Counted indentation spaces/tabs\\n- [ ] Included blank lines if they exist\\n- [ ] Matched brace/bracket positioning\\n- [ ] Included 3-5 lines of surrounding context\\n- [ ] Verified text appears exactly once (or using replace_all)\\n- [ ] Copied text character-for-character, not approximated\\n      </whitespace_checklist>\\n\\n<examples>\\n✅ Correct: Exact match with context\\n\\n```\\nold_string: \\\"func ProcessData(input string) error {\\\\n    if input == \\\\\\\"\\\\\\\" {\\\\n        return errors.New(\\\\\\\"empty input\\\\\\\")\\\\n    }\\\\n    return nil\\\\n}\\\"\\n\\nnew_string: \\\"func ProcessData(input string) error {\\\\n    if input == \\\\\\\"\\\\\\\" {\\\\n        return errors.New(\\\\\\\"empty input\\\\\\\")\\\\n    }\\\\n    // New validation\\\\n    if len(input) > 1000 {\\\\n        return errors.New(\\\\\\\"input too long\\\\\\\")\\\\n    }\\\\n    return nil\\\\n}\\\"\\n```\\n\\n❌ Incorrect: Not enough context\\n\\n```\\nold_string: \\\"return nil\\\"  // Appears many times!\\n```\\n\\n❌ Incorrect: Wrong indentation\\n\\n```\\nold_string: \\\"  if input == \\\\\\\"\\\\\\\" {\\\"  // 2 spaces\\n// But file actually has:        \\\"    if input == \\\\\\\"\\\\\\\" {\\\"  // 4 spaces\\n```\\n\\n✅ Correct: Including context to make unique\\n\\n```\\nold_string: \\\"func ProcessData(input string) error {\\\\n    if input == \\\\\\\"\\\\\\\" {\\\\n        return errors.New(\\\\\\\"empty input\\\\\\\")\\\\n    }\\\\n    return nil\\\"\\n```\\n\\n</examples>\\n\\n<windows_notes>\\n\\n- Forward slashes work throughout (C:/path/file)\\n- File permissions handled automatically\\n- Line endings converted automatically (\\\\n ↔ \\\\r\\\\n)\\n  </windows_notes>\\n\",\"parameters\":{\"properties\":{\"file_path\":{\"description\":\"The absolute path to the file to modify\",\"type\":\"string\"},\"new_string\":{\"description\":\"The text to replace it with\",\"type\":\"string\"},\"old_string\":{\"description\":\"The text to replace\",\"type\":\"string\"},\"replace_all\":{\"description\":\"Replace all occurrences of old_string (default false)\",\"type\":\"boolean\"}},\"required\":[\"file_path\",\"old_string\",\"new_string\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"multiedit\",\"strict\":false,\"description\":\"Makes multiple edits to a single file in one operation. Built on Edit tool for efficient multiple find-and-replace operations. Prefer over Edit tool for multiple edits to same file.\\n\\n<prerequisites>\\n1. Use View tool to understand file contents and context\\n2. Verify directory path is correct\\n3. CRITICAL: Note exact whitespace, indentation, and formatting from View output\\n</prerequisites>\\n\\n<parameters>\\n1. file_path: Absolute path to file (required)\\n2. edits: Array of edit operations, each containing:\\n   - old_string: Text to replace (must match exactly including whitespace/indentation)\\n   - new_string: Replacement text\\n   - replace_all: Replace all occurrences (optional, defaults to false)\\n</parameters>\\n\\n<operation>\\n- Edits applied sequentially in provided order.\\n- Each edit operates on result of previous edit.\\n- PARTIAL SUCCESS: If some edits fail, successful edits are still applied. Failed edits are returned in the response.\\n- File is modified if at least one edit succeeds.\\n- Ideal for several changes to different parts of same file.\\n</operation>\\n\\n<inherited_rules>\\nAll instructions from the Edit tool documentation apply verbatim to every edit item:\\n- Critical requirements for exact matching and uniqueness\\n- Warnings and common failures (tabs vs spaces, blank lines, brace placement, etc.)\\n- Verification steps before using, recovery steps, best practices, and whitespace checklist\\nUse the same level of precision as Edit. Multiedit often fails due to formatting mismatches—double-check whitespace for every edit.\\n</inherited_rules>\\n\\n<critical_requirements>\\n1. Apply Edit tool rules to EACH edit (see edit.md).\\n2. Edits are applied in order; successful edits are kept even if later edits fail.\\n3. Plan sequence carefully: earlier edits change the file content that later edits must match.\\n4. Ensure each old_string is unique at its application time (after prior edits).\\n5. Check the response for failed edits and retry them if needed.\\n</critical_requirements>\\n\\n<verification_before_using>\\n1. View the file and copy exact text (including whitespace) for each target.\\n2. Check how many instances each old_string has BEFORE the sequence starts.\\n3. Dry-run mentally: after applying edit #N, will edit #N+1 still match? Adjust old_string/new_string accordingly.\\n4. Prefer fewer, larger context blocks over many tiny fragments that are easy to misalign.\\n5. If edits are independent, consider separate multiedit batches per logical region.\\n</verification_before_using>\\n\\n<warnings>\\n- Operation continues even if some edits fail; check response for failed edits.\\n- Earlier edits can invalidate later matches (added/removed spaces, lines, or reordered text).\\n- Mixed tabs/spaces, trailing spaces, or missing blank lines commonly cause failures.\\n- replace_all may affect unintended regions—use carefully or provide more context.\\n</warnings>\\n\\n<recovery_steps>\\nIf some edits fail:\\n1. Check the response metadata for the list of failed edits with their error messages.\\n2. View the file again to see the current state after successful edits.\\n3. Adjust the failed edits based on the new file content.\\n4. Retry the failed edits with corrected old_string values.\\n5. Consider breaking complex batches into smaller, independent operations.\\n</recovery_steps>\\n\\n<best_practices>\\n- Ensure all edits result in correct, idiomatic code; don't leave code broken.\\n- Use absolute file paths (starting with /).\\n- Use replace_all only when you're certain; otherwise provide unique context.\\n- Match existing style exactly (spaces, tabs, blank lines).\\n- Review failed edits in the response and retry with corrections.\\n</best_practices>\\n\\n<whitespace_checklist>\\nFor EACH edit, verify:\\n- [ ] Viewed the file first\\n- [ ] Counted indentation spaces/tabs\\n- [ ] Included blank lines if present\\n- [ ] Matched brace/bracket positioning\\n- [ ] Included 3–5 lines of surrounding context\\n- [ ] Verified text appears exactly once (or using replace_all deliberately)\\n- [ ] Copied text character-for-character, not approximated\\n</whitespace_checklist>\\n\\n<examples>\\n✅ Correct: Sequential edits where the second match accounts for the first change\\n\\n```\\nedits: [\\n  {\\n    old_string: \\\"func A() {\\\\n    doOld()\\\\n}\\\",\\n    new_string: \\\"func A() {\\\\n    doNew()\\\\n}\\\",\\n  },\\n  {\\n    // Uses context that still exists AFTER the first replacement\\n    old_string: \\\"func B() {\\\\n    callA()\\\\n}\\\",\\n    new_string: \\\"func B() {\\\\n    callA()\\\\n    logChange()\\\\n}\\\",\\n  },\\n]\\n```\\n\\n❌ Incorrect: Second old_string no longer matches due to whitespace change introduced by the first edit\\n\\n```\\nedits: [\\n  {\\n    old_string: \\\"func A() {\\\\n    doOld()\\\\n}\\\",\\n    new_string: \\\"func A() {\\\\n\\\\n    doNew()\\\\n}\\\", // Added extra blank line\\n  },\\n  {\\n    old_string: \\\"func A() {\\\\n    doNew()\\\\n}\\\", // Missing the new blank line, will FAIL\\n    new_string: \\\"func A() {\\\\n    doNew()\\\\n    logChange()\\\\n}\\\",\\n  },\\n]\\n```\\n\\n✅ Correct: Handling partial success\\n\\n```\\n// If edit 2 fails, edit 1 is still applied\\n// Response will indicate:\\n// - edits_applied: 1\\n// - edits_failed: [{index: 2, error: \\\"...\\\", edit: {...}}]\\n// You can then retry edit 2 with corrected context\\n```\\n</examples>\\n\",\"parameters\":{\"properties\":{\"edits\":{\"description\":\"Array of edit operations to perform sequentially on the file\",\"items\":{\"properties\":{\"new_string\":{\"description\":\"The text to replace it with\",\"type\":\"string\"},\"old_string\":{\"description\":\"The text to replace\",\"type\":\"string\"},\"replace_all\":{\"description\":\"Replace all occurrences of old_string (default false).\",\"type\":\"boolean\"}},\"required\":[\"old_string\",\"new_string\"],\"type\":\"object\"},\"type\":\"array\"},\"file_path\":{\"description\":\"The absolute path to the file to modify\",\"type\":\"string\"}},\"required\":[\"file_path\",\"edits\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"fetch\",\"strict\":false,\"description\":\"Fetches raw content from URL and returns it in specified format without any AI processing.\\n\\n<when_to_use>\\nUse this tool when you need:\\n- Raw, unprocessed content from a URL\\n- Direct access to API responses or JSON data\\n- HTML/text/markdown content without interpretation\\n- Simple, fast content retrieval without analysis\\n- To save tokens by avoiding AI processing\\n\\nDO NOT use this tool when you need to:\\n- Extract specific information from a webpage (use agentic_fetch instead)\\n- Answer questions about web content (use agentic_fetch instead)\\n- Analyze or summarize web pages (use agentic_fetch instead)\\n</when_to_use>\\n\\n<usage>\\n- Provide URL to fetch content from\\n- Specify desired output format (text, markdown, or html)\\n- Optional timeout for request\\n</usage>\\n\\n<features>\\n- Supports three output formats: text, markdown, html\\n- Auto-handles HTTP redirects\\n- Fast and lightweight - no AI processing\\n- Sets reasonable timeouts to prevent hanging\\n- Validates input parameters before requests\\n</features>\\n\\n<limitations>\\n- Max response size: 5MB\\n- Only supports HTTP and HTTPS protocols\\n- Cannot handle authentication or cookies\\n- Some websites may block automated requests\\n- Returns raw content only - no analysis or extraction\\n</limitations>\\n\\n<tips>\\n- Use text format for plain text content or simple API responses\\n- Use markdown format for content that should be rendered with formatting\\n- Use html format when you need raw HTML structure\\n- Set appropriate timeouts for potentially slow websites\\n- If the user asks to analyze or extract from a page, use agentic_fetch instead\\n</tips>\\n\",\"parameters\":{\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html)\",\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds (max 120)\",\"type\":\"integer\"},\"url\":{\"description\":\"The URL to fetch content from\",\"type\":\"string\"}},\"required\":[\"url\",\"format\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"glob\",\"strict\":false,\"description\":\"Fast file pattern matching tool that finds files by name/pattern, returning paths sorted by modification time (newest first).\\n\\n<usage>\\n- Provide glob pattern to match against file paths\\n- Optional starting directory (defaults to current working directory)\\n- Results sorted with most recently modified files first\\n</usage>\\n\\n<pattern_syntax>\\n- '\\\\*' matches any sequence of non-separator characters\\n- '\\\\*\\\\*' matches any sequence including separators\\n- '?' matches any single non-separator character\\n- '[...]' matches any character in brackets\\n- '[!...]' matches any character not in brackets\\n</pattern_syntax>\\n\\n<examples>\\n- '*.js' - JavaScript files in current directory\\n- '**/*.js' - JavaScript files in any subdirectory\\n- 'src/**/*.{ts,tsx}' - TypeScript files in src directory\\n- '*.{html,css,js}' - HTML, CSS, and JS files\\n</examples>\\n\\n<limitations>\\n- Results limited to 100 files (newest first)\\n- Does not search file contents (use Grep for that)\\n- Hidden files (starting with '.') skipped\\n</limitations>\\n\\n<cross_platform>\\n- Path separators handled automatically (/ and \\\\ work)\\n- Uses ripgrep (rg) if available, otherwise Go implementation\\n- Patterns should use forward slashes (/) for compatibility\\n</cross_platform>\\n\\n<tips>\\n- Combine with Grep: find files with Glob, search contents with Grep\\n- For iterative exploration requiring multiple searches, consider Agent tool\\n- Check if results truncated and refine pattern if needed\\n</tips>\\n\",\"parameters\":{\"properties\":{\"path\":{\"description\":\"The directory to search in. Defaults to the current working directory.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against\",\"type\":\"string\"}},\"required\":[\"pattern\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"grep\",\"strict\":false,\"description\":\"Fast content search tool that finds files containing specific text/patterns, returning matching paths sorted by modification time (newest first).\\n\\n<usage>\\n- Provide regex pattern to search within file contents\\n- Set literal_text=true for exact text with special characters (recommended for non-regex users)\\n- Optional starting directory (defaults to current working directory)\\n- Optional include pattern to filter which files to search\\n- Results sorted with most recently modified files first\\n</usage>\\n\\n<regex_syntax>\\nWhen literal_text=false (supports standard regex):\\n\\n- 'function' searches for literal text \\\"function\\\"\\n- 'log\\\\..\\\\*Error' finds text starting with \\\"log.\\\" and ending with \\\"Error\\\"\\n- 'import\\\\s+.\\\\*\\\\s+from' finds import statements in JavaScript/TypeScript\\n</regex_syntax>\\n\\n<include_patterns>\\n- '\\\\*.js' - Only search JavaScript files\\n- '\\\\*.{ts,tsx}' - Only search TypeScript files\\n- '\\\\*.go' - Only search Go files\\n</include_patterns>\\n\\n<limitations>\\n- Results limited to 100 files (newest first)\\n- Performance depends on number of files searched\\n- Very large binary files may be skipped\\n- Hidden files (starting with '.') skipped\\n</limitations>\\n\\n<ignore_support>\\n- Respects .gitignore patterns to skip ignored files/directories\\n- Respects .crushignore patterns for additional ignore rules\\n- Both ignore files auto-detected in search root directory\\n</ignore_support>\\n\\n<cross_platform>\\n- Uses ripgrep (rg) if available for better performance\\n- Falls back to Go implementation if ripgrep unavailable\\n- File paths normalized automatically for compatibility\\n</cross_platform>\\n\\n<tips>\\n- For faster searches: use Glob to find relevant files first, then Grep\\n- For iterative exploration requiring multiple searches, consider Agent tool\\n- Check if results truncated and refine search pattern if needed\\n- Use literal_text=true for exact text with special characters (dots, parentheses, etc.)\\n</tips>\\n\",\"parameters\":{\"properties\":{\"include\":{\"description\":\"File pattern to include in the search (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\")\",\"type\":\"string\"},\"literal_text\":{\"description\":\"If true, the pattern will be treated as literal text with special regex characters escaped. Default is false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory to search in. Defaults to the current working directory.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents\",\"type\":\"string\"}},\"required\":[\"pattern\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"ls\",\"strict\":false,\"description\":\"Shows files and subdirectories in tree structure for exploring project organization.\\n\\n<usage>\\n- Provide path to list (defaults to current working directory)\\n- Optional glob patterns to ignore\\n- Results displayed in tree structure\\n</usage>\\n\\n<features>\\n- Hierarchical view of files and directories\\n- Auto-skips hidden files/directories (starting with '.')\\n- Skips common system directories like __pycache__\\n- Can filter files matching specific patterns\\n</features>\\n\\n<limitations>\\n- Results limited to 1000 files\\n- Large directories truncated\\n- No file sizes or permissions shown\\n- Cannot recursively list all directories in large projects\\n</limitations>\\n\\n<cross_platform>\\n- Hidden file detection uses Unix convention (files starting with '.')\\n- Windows hidden files (with hidden attribute) not auto-skipped\\n- Common Windows directories (System32, Program Files) not in default ignore\\n- Path separators handled automatically (/ and \\\\ work)\\n</cross_platform>\\n\\n<tips>\\n- Use Glob for finding files by name patterns instead of browsing\\n- Use Grep for searching file contents\\n- Combine with other tools for effective exploration\\n</tips>\\n\",\"parameters\":{\"properties\":{\"depth\":{\"description\":\"The maximum depth to traverse\",\"type\":\"integer\"},\"ignore\":{\"description\":\"List of glob patterns to ignore\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"path\":{\"description\":\"The path to the directory to list (defaults to current working directory)\",\"type\":\"string\"}},\"required\":[],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"sourcegraph\",\"strict\":false,\"description\":\"Search code across public repositories using Sourcegraph's GraphQL API.\\n\\n<usage>\\n- Provide search query using Sourcegraph syntax\\n- Optional result count (default: 10, max: 20)\\n- Optional timeout for request\\n</usage>\\n\\n<basic_syntax>\\n- \\\"fmt.Println\\\" - exact matches\\n- \\\"file:.go fmt.Println\\\" - limit to Go files\\n- \\\"repo:^github\\\\.com/golang/go$ fmt.Println\\\" - specific repos\\n- \\\"lang:go fmt.Println\\\" - limit to Go code\\n- \\\"fmt.Println AND log.Fatal\\\" - combined terms\\n- \\\"fmt\\\\.(Print|Printf|Println)\\\" - regex patterns\\n- \\\"\\\\\\\"exact phrase\\\\\\\"\\\" - exact phrase matching\\n- \\\"-file:test\\\" or \\\"-repo:forks\\\" - exclude matches\\n</basic_syntax>\\n\\n<key_filters>\\nRepository: repo:name, repo:^exact$, repo:org/repo@branch, -repo:exclude, fork:yes, archived:yes, visibility:public\\nFile: file:\\\\.js$, file:internal/, -file:test, file:has.content(text)\\nContent: content:\\\"exact\\\", -content:\\\"unwanted\\\", case:yes\\nType: type:symbol, type:file, type:path, type:diff, type:commit\\nTime: after:\\\"1 month ago\\\", before:\\\"2023-01-01\\\", author:name, message:\\\"fix\\\"\\nResult: select:repo, select:file, select:content, count:100, timeout:30s\\n</key_filters>\\n\\n<examples>\\n- \\\"file:.go context.WithTimeout\\\" - Go code using context.WithTimeout\\n- \\\"lang:typescript useState type:symbol\\\" - TypeScript React useState hooks\\n- \\\"repo:^github\\\\.com/kubernetes/kubernetes$ pod list type:file\\\" - Kubernetes pod files\\n- \\\"file:Dockerfile (alpine OR ubuntu) -content:alpine:latest\\\" - Dockerfiles with base images\\n</examples>\\n\\n<boolean_operators>\\n- \\\"term1 AND term2\\\" - both terms\\n- \\\"term1 OR term2\\\" - either term\\n- \\\"term1 NOT term2\\\" - term1 but not term2\\n- \\\"term1 and (term2 or term3)\\\" - grouping with parentheses\\n</boolean_operators>\\n\\n<limitations>\\n- Only searches public repositories\\n- Rate limits may apply\\n- Complex queries take longer\\n- Max 20 results per query\\n</limitations>\\n\\n<tips>\\n- Use specific file extensions to narrow results\\n- Add repo: filters for targeted searches\\n- Use type:symbol for function/method definitions\\n- Use type:file to find relevant files\\n</tips>\\n\",\"parameters\":{\"properties\":{\"context_window\":{\"description\":\"The context around the match to return (default: 10 lines)\",\"type\":\"integer\"},\"count\":{\"description\":\"Optional number of results to return (default: 10, max: 20)\",\"type\":\"integer\"},\"query\":{\"description\":\"The Sourcegraph search query\",\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds (max 120)\",\"type\":\"integer\"}},\"required\":[\"query\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"view\",\"strict\":false,\"description\":\"Reads and displays file contents with line numbers for examining code, logs, or text data.\\n\\n<usage>\\n- Provide file path to read\\n- Optional offset: start reading from specific line (0-based)\\n- Optional limit: control lines read (default 2000)\\n- Don't use for directories (use LS tool instead)\\n</usage>\\n\\n<features>\\n- Displays contents with line numbers\\n- Can read from any file position using offset\\n- Handles large files by limiting lines read\\n- Auto-truncates very long lines for display\\n- Suggests similar filenames when file not found\\n</features>\\n\\n<limitations>\\n- Max file size: 250KB\\n- Default limit: 2000 lines\\n- Lines >2000 chars truncated\\n- Cannot display binary files/images (identifies them)\\n</limitations>\\n\\n<cross_platform>\\n- Handles Windows (CRLF) and Unix (LF) line endings\\n- Works with forward slashes (/) and backslashes (\\\\)\\n- Auto-detects text encoding for common formats\\n</cross_platform>\\n\\n<tips>\\n- Use with Glob to find files first\\n- For code exploration: Grep to find relevant files, then View to examine\\n- For large files: use offset parameter for specific sections\\n</tips>\\n\",\"parameters\":{\"properties\":{\"file_path\":{\"description\":\"The path to the file to read\",\"type\":\"string\"},\"limit\":{\"description\":\"The number of lines to read (defaults to 2000)\",\"type\":\"integer\"},\"offset\":{\"description\":\"The line number to start reading from (0-based)\",\"type\":\"integer\"}},\"required\":[\"file_path\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"write\",\"strict\":false,\"description\":\"Creates or updates files in filesystem for saving/modifying text content.\\n\\n<usage>\\n- Provide file path to write\\n- Include content to write to file\\n- Tool creates necessary parent directories automatically\\n</usage>\\n\\n<features>\\n- Creates new files or overwrites existing ones\\n- Auto-creates parent directories if missing\\n- Checks if file modified since last read for safety\\n- Avoids unnecessary writes when content unchanged\\n</features>\\n\\n<limitations>\\n- Read file before writing to avoid conflicts\\n- Cannot append (rewrites entire file)\\n</limitations>\\n\\n<cross_platform>\\n- Use forward slashes (/) for compatibility\\n</cross_platform>\\n\\n<tips>\\n- Use View tool first to examine existing files before modifying\\n- Use LS tool to verify location when creating new files\\n- Combine with Glob/Grep to find and modify multiple files\\n- Include descriptive comments when changing existing code\\n</tips>\\n\",\"parameters\":{\"properties\":{\"content\":{\"description\":\"The content to write to the file\",\"type\":\"string\"},\"file_path\":{\"description\":\"The path to the file to write\",\"type\":\"string\"}},\"required\":[\"file_path\",\"content\"],\"type\":\"object\"}},\"type\":\"function\"}],\"stream\":true}"
    headers:
      Accept:
      - application/json
      Content-Type:
      - application/json
      User-Agent:
      - OpenAI/Go 2.7.1
    url: https://api.openai.com/v1/chat/completions
    method: POST
  response:
    proto: HTTP/2.0
    proto_major: 2
    proto_minor: 0
    content_length: -1
    body: |+
      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_PWjoW0TdkwQWa1uISDjyGwVy","type":"function","function":{"name":"sourcegraph","arguments":""}}],"refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"bRB81"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"04uHylOZoGPtZwT"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"query"}}]},"finish_reason":null}],"usage":null,"obfuscation":"m6WxDCNuJCKQg"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"4EoPjneWlm0XV"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"lang"}}]},"finish_reason":null}],"usage":null,"obfuscation":"Eziu1mvUA2ZfEI"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"F"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"go"}}]},"finish_reason":null}],"usage":null,"obfuscation":""}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" content"}}]},"finish_reason":null}],"usage":null,"obfuscation":"i3haZQNvch"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":":\\\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"XXzNIuqrgue5g"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"func"}}]},"finish_reason":null}],"usage":null,"obfuscation":"tC1GuhGAsGSPY6"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" main"}}]},"finish_reason":null}],"usage":null,"obfuscation":"i7vo5mRQBIdfM"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"(\\\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"2DBvRqvVjEbdx"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"TaE7ZMDaQxz04"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"count"}}]},"finish_reason":null}],"usage":null,"obfuscation":"PoW08ezQfMmwn"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"AeQjhLxXKcfnHcM"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"10"}}]},"finish_reason":null}],"usage":null,"obfuscation":""}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":",\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"NSAINR0ilO1JoeD"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"context"}}]},"finish_reason":null}],"usage":null,"obfuscation":"i7M6clVTbQO"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"_window"}}]},"finish_reason":null}],"usage":null,"obfuscation":"SIwrKwHaGvE"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"6KE0DzBWjLfhTBx"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"5"}}]},"finish_reason":null}],"usage":null,"obfuscation":"d"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":",\""}}]},"finish_reason":null}],"usage":null,"obfuscation":"TUGTlVUNUkhqKHo"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"timeout"}}]},"finish_reason":null}],"usage":null,"obfuscation":"RtCKz5fptMj"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":"}}]},"finish_reason":null}],"usage":null,"obfuscation":"GxW6aNbd4c4D6K1"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"60"}}]},"finish_reason":null}],"usage":null,"obfuscation":""}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}"}}]},"finish_reason":null}],"usage":null,"obfuscation":"J"}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":null,"obfuscation":""}

      data: {"id":"chatcmpl-CbUm9ovGwI2bocyKqwXTVW9gxmgCU","object":"chat.completion.chunk","created":1763052473,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":10341,"completion_tokens":235,"total_tokens":10576,"prompt_tokens_details":{"cached_tokens":2688,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":192,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"WSlJTuYABu7bSWl"}

      data: [DONE]

    headers:
      Content-Type:
      - text/event-stream; charset=utf-8
    status: 200 OK
    code: 200
    duration: 5.6707765s
- id: 2
  request:
    proto: HTTP/1.1
    proto_major: 1
    proto_minor: 1
    content_length: 437
    host: sourcegraph.com
    body: '{"query":"query Search($query: String!) { search(query: $query, version: V2, patternType: keyword ) { results { matchCount, limitHit, resultCount, approximateResultCount, missing { name }, timedout { name }, indexUnavailable, results { __typename, ... on FileMatch { repository { name }, file { path, url, content }, lineMatches { preview, lineNumber, offsetAndLengths } } } } } }","variables":{"query":"lang:go content:\"func main(\""}}'
    headers:
      Content-Type:
      - application/json
      User-Agent:
      - crush/1.0
    url: https://sourcegraph.com/.api/graphql
    method: POST
  response:
    proto: HTTP/2.0
    proto_major: 2
    proto_minor: 0
    content_length: -1
    uncompressed: true
    body: "{\"data\":{\"search\":{\"results\":{\"matchCount\":30,\"limitHit\":true,\"resultCount\":30,\"approximateResultCount\":\"30+\",\"missing\":[],\"timedout\":[],\"indexUnavailable\":false,\"results\":[{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum\"},\"file\":{\"path\":\"cmd/wnode/main.go\",\"url\":\"/github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum/-/blob/cmd/wnode/main.go\",\"content\":\"\\n//此源码被清华学神尹成大魔王专业翻译分析并修改\\n//尹成QQ77025077\\n//尹成微信18510341407\\n//尹成所在QQ群721929980\\n//尹成邮箱 yinc13@mails.tsinghua.edu.cn\\n//尹成毕业于清华大学,微软区块链领域全球最有价值专家\\n//https://mvp.microsoft.com/zh-cn/PublicProfile/4033620\\n//版权所有2017 Go Ethereum作者\\n//此文件是Go以太坊的一部分。\\n//\\n//Go以太坊是免费软件：您可以重新发布和/或修改它\\n//根据GNU通用公共许可证的条款\\n//自由软件基金会，或者许可证的第3版，或者\\n//（由您选择）任何更高版本。\\n//\\n//Go以太坊的分布希望它会有用，\\n//但没有任何保证；甚至没有\\n//适销性或特定用途的适用性。见\\n//GNU通用公共许可证了解更多详细信息。\\n//\\n//你应该已经收到一份GNU通用公共许可证的副本\\n//一起去以太坊吧。如果没有，请参见\\u003chttp://www.gnu.org/licenses/\\u003e。\\n\\n//这是一个简单的耳语节点。它可以用作独立的引导节点。\\n//也可用于不同的测试和诊断目的。\\n\\npackage main\\n\\nimport (\\n\\t\\\"bufio\\\"\\n\\t\\\"crypto/ecdsa\\\"\\n\\tcrand \\\"crypto/rand\\\"\\n\\t\\\"crypto/sha512\\\"\\n\\t\\\"encoding/binary\\\"\\n\\t\\\"encoding/hex\\\"\\n\\t\\\"flag\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"os\\\"\\n\\t\\\"path/filepath\\\"\\n\\t\\\"strconv\\\"\\n\\t\\\"strings\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/ethereum/go-ethereum/cmd/utils\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/common\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/console\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/crypto\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/log\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/p2p\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/p2p/discover\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/p2p/nat\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/whisper/mailserver\\\"\\n\\twhisper \\\"github.com/ethereum/go-ethereum/whisper/whisperv6\\\"\\n\\t\\\"golang.org/x/crypto/pbkdf2\\\"\\n)\\n\\nconst quitCommand = \\\"~Q\\\"\\nconst entropySize = 32\\n\\n//独生子女\\nvar (\\n\\tserver     *p2p.Server\\n\\tshh        *whisper.Whisper\\n\\tdone       chan struct{}\\n\\tmailServer mailserver.WMailServer\\n\\tentropy    [entropySize]byte\\n\\n\\tinput = bufio.NewReader(os.Stdin)\\n)\\n\\n//加密\\nvar (\\n\\tsymKey  []byte\\n\\tpub     *ecdsa.PublicKey\\n\\tasymKey *ecdsa.PrivateKey\\n\\tnodeid  *ecdsa.PrivateKey\\n\\ttopic   whisper.TopicType\\n\\n\\tasymKeyID    string\\n\\tasymFilterID string\\n\\tsymFilterID  string\\n\\tsymPass      string\\n\\tmsPassword   string\\n)\\n\\n//CMD参数\\nvar (\\n\\tbootstrapMode  = flag.Bool(\\\"standalone\\\", false, \\\"boostrap node: don't initiate connection to peers, just wait for incoming connections\\\")\\n\\tforwarderMode  = flag.Bool(\\\"forwarder\\\", false, \\\"forwarder mode: only forward messages, neither encrypt nor decrypt messages\\\")\\n\\tmailServerMode = flag.Bool(\\\"mailserver\\\", false, \\\"mail server mode: delivers expired messages on demand\\\")\\n\\trequestMail    = flag.Bool(\\\"mailclient\\\", false, \\\"request expired messages from the bootstrap server\\\")\\n\\tasymmetricMode = flag.Bool(\\\"asym\\\", false, \\\"use asymmetric encryption\\\")\\n\\tgenerateKey    = flag.Bool(\\\"generatekey\\\", false, \\\"generate and show the private key\\\")\\n\\tfileExMode     = flag.Bool(\\\"fileexchange\\\", false, \\\"file exchange mode\\\")\\n\\tfileReader     = flag.Bool(\\\"filereader\\\", false, \\\"load and decrypt messages saved as files, display as plain text\\\")\\n\\ttestMode       = flag.Bool(\\\"test\\\", false, \\\"use of predefined parameters for diagnostics (password, etc.)\\\")\\n\\techoMode       = flag.Bool(\\\"echo\\\", false, \\\"echo mode: prints some arguments for diagnostics\\\")\\n\\n\\targVerbosity = flag.Int(\\\"verbosity\\\", int(log.LvlError), \\\"log verbosity level\\\")\\n\\targTTL       = flag.Uint(\\\"ttl\\\", 30, \\\"time-to-live for messages in seconds\\\")\\n\\targWorkTime  = flag.Uint(\\\"work\\\", 5, \\\"work time in seconds\\\")\\n\\targMaxSize   = flag.Uint(\\\"maxsize\\\", uint(whisper.DefaultMaxMessageSize), \\\"max size of message\\\")\\n\\targPoW       = flag.Float64(\\\"pow\\\", whisper.DefaultMinimumPoW, \\\"PoW for normal messages in float format (e.g. 2.7)\\\")\\n\\targServerPoW = flag.Float64(\\\"mspow\\\", whisper.DefaultMinimumPoW, \\\"PoW requirement for Mail Server request\\\")\\n\\n\\targIP      = flag.String(\\\"ip\\\", \\\"\\\", \\\"IP address and port of this node (e.g. 127.0.0.1:30303)\\\")\\n\\targPub     = flag.String(\\\"pub\\\", \\\"\\\", \\\"public key for asymmetric encryption\\\")\\n\\targDBPath  = flag.String(\\\"dbpath\\\", \\\"\\\", \\\"path to the server's DB directory\\\")\\n\\targIDFile  = flag.String(\\\"idfile\\\", \\\"\\\", \\\"file name with node id (private key)\\\")\\nargEnode   = flag.String(\\\"boot\\\", \\\"\\\", \\\"bootstrap node you want to connect to (e.g. enode://E454……08D50@52.176.211.200:16428）”）\\n\\targTopic   = flag.String(\\\"topic\\\", \\\"\\\", \\\"topic in hexadecimal format (e.g. 70a4beef)\\\")\\n\\targSaveDir = flag.String(\\\"savedir\\\", \\\"\\\", \\\"directory where all incoming messages will be saved as files\\\")\\n)\\n\\nfunc main() {\\n\\tprocessArgs()\\n\\tinitialize()\\n\\trun()\\n\\tshutdown()\\n}\\n\\nfunc processArgs() {\\n\\tflag.Parse()\\n\\n\\tif len(*argIDFile) \\u003e 0 {\\n\\t\\tvar err error\\n\\t\\tnodeid, err = crypto.LoadECDSA(*argIDFile)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to load file [%s]: %s.\\\", *argIDFile, err)\\n\\t\\t}\\n\\t}\\n\\nconst enodePrefix = \\\"enode://“\\n\\tif len(*argEnode) \\u003e 0 {\\n\\t\\tif (*argEnode)[:len(enodePrefix)] != enodePrefix {\\n\\t\\t\\t*argEnode = enodePrefix + *argEnode\\n\\t\\t}\\n\\t}\\n\\n\\tif len(*argTopic) \\u003e 0 {\\n\\t\\tx, err := hex.DecodeString(*argTopic)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to parse the topic: %s\\\", err)\\n\\t\\t}\\n\\t\\ttopic = whisper.BytesToTopic(x)\\n\\t}\\n\\n\\tif *asymmetricMode \\u0026\\u0026 len(*argPub) \\u003e 0 {\\n\\t\\tvar err error\\n\\t\\tif pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"invalid public key\\\")\\n\\t\\t}\\n\\t}\\n\\n\\tif len(*argSaveDir) \\u003e 0 {\\n\\t\\tif _, err := os.Stat(*argSaveDir); os.IsNotExist(err) {\\n\\t\\t\\tutils.Fatalf(\\\"Download directory '%s' does not exist\\\", *argSaveDir)\\n\\t\\t}\\n\\t} else if *fileExMode {\\n\\t\\tutils.Fatalf(\\\"Parameter 'savedir' is mandatory for file exchange mode\\\")\\n\\t}\\n\\n\\tif *echoMode {\\n\\t\\techo()\\n\\t}\\n}\\n\\nfunc echo() {\\n\\tfmt.Printf(\\\"ttl = %d \\\\n\\\", *argTTL)\\n\\tfmt.Printf(\\\"workTime = %d \\\\n\\\", *argWorkTime)\\n\\tfmt.Printf(\\\"pow = %f \\\\n\\\", *argPoW)\\n\\tfmt.Printf(\\\"mspow = %f \\\\n\\\", *argServerPoW)\\n\\tfmt.Printf(\\\"ip = %s \\\\n\\\", *argIP)\\n\\tfmt.Printf(\\\"pub = %s \\\\n\\\", common.ToHex(crypto.FromECDSAPub(pub)))\\n\\tfmt.Printf(\\\"idfile = %s \\\\n\\\", *argIDFile)\\n\\tfmt.Printf(\\\"dbpath = %s \\\\n\\\", *argDBPath)\\n\\tfmt.Printf(\\\"boot = %s \\\\n\\\", *argEnode)\\n}\\n\\nfunc initialize() {\\n\\tlog.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))\\n\\n\\tdone = make(chan struct{})\\n\\tvar peers []*discover.Node\\n\\tvar err error\\n\\n\\tif *generateKey {\\n\\t\\tkey, err := crypto.GenerateKey()\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to generate private key: %s\\\", err)\\n\\t\\t}\\n\\t\\tk := hex.EncodeToString(crypto.FromECDSA(key))\\n\\t\\tfmt.Printf(\\\"Random private key: %s \\\\n\\\", k)\\n\\t\\tos.Exit(0)\\n\\t}\\n\\n\\tif *testMode {\\nsymPass = \\\"wwww\\\" //ASCII码：0x77777777\\n\\t\\tmsPassword = \\\"wwww\\\"\\n\\t}\\n\\n\\tif *bootstrapMode {\\n\\t\\tif len(*argIP) == 0 {\\n\\t\\t\\targIP = scanLineA(\\\"Please enter your IP and port (e.g. 127.0.0.1:30348): \\\")\\n\\t\\t}\\n\\t} else if *fileReader {\\n\\t\\t*bootstrapMode = true\\n\\t} else {\\n\\t\\tif len(*argEnode) == 0 {\\n\\t\\t\\targEnode = scanLineA(\\\"Please enter the peer's enode: \\\")\\n\\t\\t}\\n\\t\\tpeer := discover.MustParseNode(*argEnode)\\n\\t\\tpeers = append(peers, peer)\\n\\t}\\n\\n\\tif *mailServerMode {\\n\\t\\tif len(msPassword) == 0 {\\n\\t\\t\\tmsPassword, err = console.Stdin.PromptPassword(\\\"Please enter the Mail Server password: \\\")\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Failed to read Mail Server password: %s\\\", err)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\tcfg := \\u0026whisper.Config{\\n\\t\\tMaxMessageSize:     uint32(*argMaxSize),\\n\\t\\tMinimumAcceptedPOW: *argPoW,\\n\\t}\\n\\n\\tshh = whisper.New(cfg)\\n\\n\\tif *argPoW != whisper.DefaultMinimumPoW {\\n\\t\\terr := shh.SetMinimumPoW(*argPoW)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to set PoW: %s\\\", err)\\n\\t\\t}\\n\\t}\\n\\n\\tif uint32(*argMaxSize) != whisper.DefaultMaxMessageSize {\\n\\t\\terr := shh.SetMaxMessageSize(uint32(*argMaxSize))\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to set max message size: %s\\\", err)\\n\\t\\t}\\n\\t}\\n\\n\\tasymKeyID, err = shh.NewKeyPair()\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to generate a new key pair: %s\\\", err)\\n\\t}\\n\\n\\tasymKey, err = shh.GetPrivateKey(asymKeyID)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to retrieve a new key pair: %s\\\", err)\\n\\t}\\n\\n\\tif nodeid == nil {\\n\\t\\ttmpID, err := shh.NewKeyPair()\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to generate a new key pair: %s\\\", err)\\n\\t\\t}\\n\\n\\t\\tnodeid, err = shh.GetPrivateKey(tmpID)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to retrieve a new key pair: %s\\\", err)\\n\\t\\t}\\n\\t}\\n\\n\\tmaxPeers := 80\\n\\tif *bootstrapMode {\\n\\t\\tmaxPeers = 800\\n\\t}\\n\\n\\t_, err = crand.Read(entropy[:])\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"crypto/rand failed: %s\\\", err)\\n\\t}\\n\\n\\tif *mailServerMode {\\n\\t\\tshh.RegisterServer(\\u0026mailServer)\\n\\t\\tif err := mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW); err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to init MailServer: %s\\\", err)\\n\\t\\t}\\n\\t}\\n\\n\\tserver = \\u0026p2p.Server{\\n\\t\\tConfig: p2p.Config{\\n\\t\\t\\tPrivateKey:     nodeid,\\n\\t\\t\\tMaxPeers:       maxPeers,\\n\\t\\t\\tName:           common.MakeName(\\\"wnode\\\", \\\"6.0\\\"),\\n\\t\\t\\tProtocols:      shh.Protocols(),\\n\\t\\t\\tListenAddr:     *argIP,\\n\\t\\t\\tNAT:            nat.Any(),\\n\\t\\t\\tBootstrapNodes: peers,\\n\\t\\t\\tStaticNodes:    peers,\\n\\t\\t\\tTrustedNodes:   peers,\\n\\t\\t},\\n\\t}\\n}\\n\\nfunc startServer() error {\\n\\terr := server.Start()\\n\\tif err != nil {\\n\\t\\tfmt.Printf(\\\"Failed to start Whisper peer: %s.\\\", err)\\n\\t\\treturn err\\n\\t}\\n\\n\\tfmt.Printf(\\\"my public key: %s \\\\n\\\", common.ToHex(crypto.FromECDSAPub(\\u0026asymKey.PublicKey)))\\n\\tfmt.Println(server.NodeInfo().Enode)\\n\\n\\tif *bootstrapMode {\\n\\t\\tconfigureNode()\\n\\t\\tfmt.Println(\\\"Bootstrap Whisper node started\\\")\\n\\t} else {\\n\\t\\tfmt.Println(\\\"Whisper node started\\\")\\n//首先看看我们是否可以建立连接，然后请求用户输入\\n\\t\\twaitForConnection(true)\\n\\t\\tconfigureNode()\\n\\t}\\n\\n\\tif *fileExMode {\\n\\t\\tfmt.Printf(\\\"Please type the file name to be send. To quit type: '%s'\\\\n\\\", quitCommand)\\n\\t} else if *fileReader {\\n\\t\\tfmt.Printf(\\\"Please type the file name to be decrypted. To quit type: '%s'\\\\n\\\", quitCommand)\\n\\t} else if !*forwarderMode {\\n\\t\\tfmt.Printf(\\\"Please type the message. To quit type: '%s'\\\\n\\\", quitCommand)\\n\\t}\\n\\treturn nil\\n}\\n\\nfunc configureNode() {\\n\\tvar err error\\n\\tvar p2pAccept bool\\n\\n\\tif *forwarderMode {\\n\\t\\treturn\\n\\t}\\n\\n\\tif *asymmetricMode {\\n\\t\\tif len(*argPub) == 0 {\\n\\t\\t\\ts := scanLine(\\\"Please enter the peer's public key: \\\")\\n\\t\\t\\tb := common.FromHex(s)\\n\\t\\t\\tif b == nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Error: can not convert hexadecimal string\\\")\\n\\t\\t\\t}\\n\\t\\t\\tif pub, err = crypto.UnmarshalPubkey(b); err != nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Error: invalid peer public key\\\")\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\tif *requestMail {\\n\\t\\tp2pAccept = true\\n\\t\\tif len(msPassword) == 0 {\\n\\t\\t\\tmsPassword, err = console.Stdin.PromptPassword(\\\"Please enter the Mail Server password: \\\")\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Failed to read Mail Server password: %s\\\", err)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\tif !*asymmetricMode \\u0026\\u0026 !*forwarderMode {\\n\\t\\tif len(symPass) == 0 {\\n\\t\\t\\tsymPass, err = console.Stdin.PromptPassword(\\\"Please enter the password for symmetric encryption: \\\")\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Failed to read passphrase: %v\\\", err)\\n\\t\\t\\t}\\n\\t\\t}\\n\\n\\t\\tsymKeyID, err := shh.AddSymKeyFromPassword(symPass)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to create symmetric key: %s\\\", err)\\n\\t\\t}\\n\\t\\tsymKey, err = shh.GetSymKey(symKeyID)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to save symmetric key: %s\\\", err)\\n\\t\\t}\\n\\t\\tif len(*argTopic) == 0 {\\n\\t\\t\\tgenerateTopic([]byte(symPass))\\n\\t\\t}\\n\\n\\t\\tfmt.Printf(\\\"Filter is configured for the topic: %x \\\\n\\\", topic)\\n\\t}\\n\\n\\tif *mailServerMode {\\n\\t\\tif len(*argDBPath) == 0 {\\n\\t\\t\\targDBPath = scanLineA(\\\"Please enter the path to DB file: \\\")\\n\\t\\t}\\n\\t}\\n\\n\\tsymFilter := whisper.Filter{\\n\\t\\tKeySym:   symKey,\\n\\t\\tTopics:   [][]byte{topic[:]},\\n\\t\\tAllowP2P: p2pAccept,\\n\\t}\\n\\tsymFilterID, err = shh.Subscribe(\\u0026symFilter)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to install filter: %s\\\", err)\\n\\t}\\n\\n\\tasymFilter := whisper.Filter{\\n\\t\\tKeyAsym:  asymKey,\\n\\t\\tTopics:   [][]byte{topic[:]},\\n\\t\\tAllowP2P: p2pAccept,\\n\\t}\\n\\tasymFilterID, err = shh.Subscribe(\\u0026asymFilter)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to install filter: %s\\\", err)\\n\\t}\\n}\\n\\nfunc generateTopic(password []byte) {\\n\\tx := pbkdf2.Key(password, password, 4096, 128, sha512.New)\\n\\tfor i := 0; i \\u003c len(x); i++ {\\n\\t\\ttopic[i%whisper.TopicLength] ^= x[i]\\n\\t}\\n}\\n\\nfunc waitForConnection(timeout bool) {\\n\\tvar cnt int\\n\\tvar connected bool\\n\\tfor !connected {\\n\\t\\ttime.Sleep(time.Millisecond * 50)\\n\\t\\tconnected = server.PeerCount() \\u003e 0\\n\\t\\tif timeout {\\n\\t\\t\\tcnt++\\n\\t\\t\\tif cnt \\u003e 1000 {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Timeout expired, failed to connect\\\")\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\tfmt.Println(\\\"Connected to peer.\\\")\\n}\\n\\nfunc run() {\\n\\terr := startServer()\\n\\tif err != nil {\\n\\t\\treturn\\n\\t}\\n\\tdefer server.Stop()\\n\\tshh.Start(nil)\\n\\tdefer shh.Stop()\\n\\n\\tif !*forwarderMode {\\n\\t\\tgo messageLoop()\\n\\t}\\n\\n\\tif *requestMail {\\n\\t\\trequestExpiredMessagesLoop()\\n\\t} else if *fileExMode {\\n\\t\\tsendFilesLoop()\\n\\t} else if *fileReader {\\n\\t\\tfileReaderLoop()\\n\\t} else {\\n\\t\\tsendLoop()\\n\\t}\\n}\\n\\nfunc shutdown() {\\n\\tclose(done)\\n\\tmailServer.Close()\\n}\\n\\nfunc sendLoop() {\\n\\tfor {\\n\\t\\ts := scanLine(\\\"\\\")\\n\\t\\tif s == quitCommand {\\n\\t\\t\\tfmt.Println(\\\"Quit command received\\\")\\n\\t\\t\\treturn\\n\\t\\t}\\n\\t\\tsendMsg([]byte(s))\\n\\t\\tif *asymmetricMode {\\n//为方便起见，请打印您自己的消息，\\n//因为在非对称模式下是不可能解密的\\n\\t\\t\\ttimestamp := time.Now().Unix()\\n\\t\\t\\tfrom := crypto.PubkeyToAddress(asymKey.PublicKey)\\n\\t\\t\\tfmt.Printf(\\\"\\\\n%d \\u003c%x\\u003e: %s\\\\n\\\", timestamp, from, s)\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc sendFilesLoop() {\\n\\tfor {\\n\\t\\ts := scanLine(\\\"\\\")\\n\\t\\tif s == quitCommand {\\n\\t\\t\\tfmt.Println(\\\"Quit command received\\\")\\n\\t\\t\\treturn\\n\\t\\t}\\n\\t\\tb, err := ioutil.ReadFile(s)\\n\\t\\tif err != nil {\\n\\t\\t\\tfmt.Printf(\\\"\\u003e\\u003e\\u003e Error: %s \\\\n\\\", err)\\n\\t\\t} else {\\n\\t\\t\\th := sendMsg(b)\\n\\t\\t\\tif (h == common.Hash{}) {\\n\\t\\t\\t\\tfmt.Printf(\\\"\\u003e\\u003e\\u003e Error: message was not sent \\\\n\\\")\\n\\t\\t\\t} else {\\n\\t\\t\\t\\ttimestamp := time.Now().Unix()\\n\\t\\t\\t\\tfrom := crypto.PubkeyToAddress(asymKey.PublicKey)\\n\\t\\t\\t\\tfmt.Printf(\\\"\\\\n%d \\u003c%x\\u003e: sent message with hash %x\\\\n\\\", timestamp, from, h)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc fileReaderLoop() {\\n\\twatcher1 := shh.GetFilter(symFilterID)\\n\\twatcher2 := shh.GetFilter(asymFilterID)\\n\\tif watcher1 == nil \\u0026\\u0026 watcher2 == nil {\\n\\t\\tfmt.Println(\\\"Error: neither symmetric nor asymmetric filter is installed\\\")\\n\\t\\treturn\\n\\t}\\n\\n\\tfor {\\n\\t\\ts := scanLine(\\\"\\\")\\n\\t\\tif s == quitCommand {\\n\\t\\t\\tfmt.Println(\\\"Quit command received\\\")\\n\\t\\t\\treturn\\n\\t\\t}\\n\\t\\traw, err := ioutil.ReadFile(s)\\n\\t\\tif err != nil {\\n\\t\\t\\tfmt.Printf(\\\"\\u003e\\u003e\\u003e Error: %s \\\\n\\\", err)\\n\\t\\t} else {\\nenv := whisper.Envelope{Data: raw} //主题为零\\nmsg := env.Open(watcher1)          //不管主题如何，都强制打开信封\\n\\t\\t\\tif msg == nil {\\n\\t\\t\\t\\tmsg = env.Open(watcher2)\\n\\t\\t\\t}\\n\\t\\t\\tif msg == nil {\\n\\t\\t\\t\\tfmt.Printf(\\\"\\u003e\\u003e\\u003e Error: failed to decrypt the message \\\\n\\\")\\n\\t\\t\\t} else {\\n\\t\\t\\t\\tprintMessageInfo(msg)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc scanLine(prompt string) string {\\n\\tif len(prompt) \\u003e 0 {\\n\\t\\tfmt.Print(prompt)\\n\\t}\\n\\ttxt, err := input.ReadString('\\\\n')\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"input error: %s\\\", err)\\n\\t}\\n\\ttxt = strings.TrimRight(txt, \\\"\\\\n\\\\r\\\")\\n\\treturn txt\\n}\\n\\nfunc scanLineA(prompt string) *string {\\n\\ts := scanLine(prompt)\\n\\treturn \\u0026s\\n}\\n\\nfunc scanUint(prompt string) uint32 {\\n\\ts := scanLine(prompt)\\n\\ti, err := strconv.Atoi(s)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Fail to parse the lower time limit: %s\\\", err)\\n\\t}\\n\\treturn uint32(i)\\n}\\n\\nfunc sendMsg(payload []byte) common.Hash {\\n\\tparams := whisper.MessageParams{\\n\\t\\tSrc:      asymKey,\\n\\t\\tDst:      pub,\\n\\t\\tKeySym:   symKey,\\n\\t\\tPayload:  payload,\\n\\t\\tTopic:    topic,\\n\\t\\tTTL:      uint32(*argTTL),\\n\\t\\tPoW:      *argPoW,\\n\\t\\tWorkTime: uint32(*argWorkTime),\\n\\t}\\n\\n\\tmsg, err := whisper.NewSentMessage(\\u0026params)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"failed to create new message: %s\\\", err)\\n\\t}\\n\\n\\tenvelope, err := msg.Wrap(\\u0026params)\\n\\tif err != nil {\\n\\t\\tfmt.Printf(\\\"failed to seal message: %v \\\\n\\\", err)\\n\\t\\treturn common.Hash{}\\n\\t}\\n\\n\\terr = shh.Send(envelope)\\n\\tif err != nil {\\n\\t\\tfmt.Printf(\\\"failed to send message: %v \\\\n\\\", err)\\n\\t\\treturn common.Hash{}\\n\\t}\\n\\n\\treturn envelope.Hash()\\n}\\n\\nfunc messageLoop() {\\n\\tsf := shh.GetFilter(symFilterID)\\n\\tif sf == nil {\\n\\t\\tutils.Fatalf(\\\"symmetric filter is not installed\\\")\\n\\t}\\n\\n\\taf := shh.GetFilter(asymFilterID)\\n\\tif af == nil {\\n\\t\\tutils.Fatalf(\\\"asymmetric filter is not installed\\\")\\n\\t}\\n\\n\\tticker := time.NewTicker(time.Millisecond * 50)\\n\\n\\tfor {\\n\\t\\tselect {\\n\\t\\tcase \\u003c-ticker.C:\\n\\t\\t\\tm1 := sf.Retrieve()\\n\\t\\t\\tm2 := af.Retrieve()\\n\\t\\t\\tmessages := append(m1, m2...)\\n\\t\\t\\tfor _, msg := range messages {\\n\\t\\t\\t\\treportedOnce := false\\n\\t\\t\\t\\tif !*fileExMode \\u0026\\u0026 len(msg.Payload) \\u003c= 2048 {\\n\\t\\t\\t\\t\\tprintMessageInfo(msg)\\n\\t\\t\\t\\t\\treportedOnce = true\\n\\t\\t\\t\\t}\\n\\n//在指定argsavedir时保存所有消息。\\n//\\n//如果fileexmode==true，则只显示哈希值，因为消息可能太大。\\n\\t\\t\\t\\tif len(*argSaveDir) \\u003e 0 {\\n\\t\\t\\t\\t\\twriteMessageToFile(*argSaveDir, msg, !reportedOnce)\\n\\t\\t\\t\\t}\\n\\t\\t\\t}\\n\\t\\tcase \\u003c-done:\\n\\t\\t\\treturn\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc printMessageInfo(msg *whisper.ReceivedMessage) {\\ntimestamp := fmt.Sprintf(\\\"%d\\\", msg.Sent) //用于诊断的Unix时间戳\\n\\ttext := string(msg.Payload)\\n\\n\\tvar address common.Address\\n\\tif msg.Src != nil {\\n\\t\\taddress = crypto.PubkeyToAddress(*msg.Src)\\n\\t}\\n\\n\\tif whisper.IsPubKeyEqual(msg.Src, \\u0026asymKey.PublicKey) {\\nfmt.Printf(\\\"\\\\n%s \\u003c%x\\u003e: %s\\\\n\\\", timestamp, address, text) //我的信息\\n\\t} else {\\nfmt.Printf(\\\"\\\\n%s [%x]: %s\\\\n\\\", timestamp, address, text) //\\n\\t}\\n}\\n\\nfunc writeMessageToFile(dir string, msg *whisper.ReceivedMessage, show bool) {\\n\\tif len(dir) == 0 {\\n\\t\\treturn\\n\\t}\\n\\n\\ttimestamp := fmt.Sprintf(\\\"%d\\\", msg.Sent)\\n\\tname := fmt.Sprintf(\\\"%x\\\", msg.EnvelopeHash)\\n\\n\\tvar address common.Address\\n\\tif msg.Src != nil {\\n\\t\\taddress = crypto.PubkeyToAddress(*msg.Src)\\n\\t}\\n\\n\\tenv := shh.GetEnvelope(msg.EnvelopeHash)\\n\\tif env == nil {\\n\\t\\tfmt.Printf(\\\"\\\\nUnexpected error: envelope not found: %x\\\\n\\\", msg.EnvelopeHash)\\n\\t\\treturn\\n\\t}\\n\\n//这是一个示例代码；如果不想保存自己的消息，请取消注释。\\n//如果whisper.ispubkeyequal（msg.src，\\u0026asymkey.publickey）\\n//fmt.printf（“\\\\n%s\\u003c%x\\u003e：我收到的消息，未保存：“%s'\\\\n”，时间戳，地址，名称）\\n//返回\\n//}\\n\\n\\tfullpath := filepath.Join(dir, name)\\n\\terr := ioutil.WriteFile(fullpath, env.Data, 0644)\\n\\tif err != nil {\\n\\t\\tfmt.Printf(\\\"\\\\n%s {%x}: message received but not saved: %s\\\\n\\\", timestamp, address, err)\\n\\t} else if show {\\n\\t\\tfmt.Printf(\\\"\\\\n%s {%x}: message received and saved as '%s' (%d bytes)\\\\n\\\", timestamp, address, name, len(env.Data))\\n\\t}\\n}\\n\\nfunc requestExpiredMessagesLoop() {\\n\\tvar key, peerID, bloom []byte\\n\\tvar timeLow, timeUpp uint32\\n\\tvar t string\\n\\tvar xt whisper.TopicType\\n\\n\\tkeyID, err := shh.AddSymKeyFromPassword(msPassword)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to create symmetric key for mail request: %s\\\", err)\\n\\t}\\n\\tkey, err = shh.GetSymKey(keyID)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to save symmetric key for mail request: %s\\\", err)\\n\\t}\\n\\tpeerID = extractIDFromEnode(*argEnode)\\n\\tshh.AllowP2PMessagesFromPeer(peerID)\\n\\n\\tfor {\\n\\t\\ttimeLow = scanUint(\\\"Please enter the lower limit of the time range (unix timestamp): \\\")\\n\\t\\ttimeUpp = scanUint(\\\"Please enter the upper limit of the time range (unix timestamp): \\\")\\n\\t\\tt = scanLine(\\\"Enter the topic (hex). Press enter to request all messages, regardless of the topic: \\\")\\n\\t\\tif len(t) == whisper.TopicLength*2 {\\n\\t\\t\\tx, err := hex.DecodeString(t)\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\tfmt.Printf(\\\"Failed to parse the topic: %s \\\\n\\\", err)\\n\\t\\t\\t\\tcontinue\\n\\t\\t\\t}\\n\\t\\t\\txt = whisper.BytesToTopic(x)\\n\\t\\t\\tbloom = whisper.TopicToBloom(xt)\\n\\t\\t\\tobfuscateBloom(bloom)\\n\\t\\t} else if len(t) == 0 {\\n\\t\\t\\tbloom = whisper.MakeFullNodeBloom()\\n\\t\\t} else {\\n\\t\\t\\tfmt.Println(\\\"Error: topic is invalid, request aborted\\\")\\n\\t\\t\\tcontinue\\n\\t\\t}\\n\\n\\t\\tif timeUpp == 0 {\\n\\t\\t\\ttimeUpp = 0xFFFFFFFF\\n\\t\\t}\\n\\n\\t\\tdata := make([]byte, 8, 8+whisper.BloomFilterSize)\\n\\t\\tbinary.BigEndian.PutUint32(data, timeLow)\\n\\t\\tbinary.BigEndian.PutUint32(data[4:], timeUpp)\\n\\t\\tdata = append(data, bloom...)\\n\\n\\t\\tvar params whisper.MessageParams\\n\\t\\tparams.PoW = *argServerPoW\\n\\t\\tparams.Payload = data\\n\\t\\tparams.KeySym = key\\n\\t\\tparams.Src = asymKey\\n\\t\\tparams.WorkTime = 5\\n\\n\\t\\tmsg, err := whisper.NewSentMessage(\\u0026params)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"failed to create new message: %s\\\", err)\\n\\t\\t}\\n\\t\\tenv, err := msg.Wrap(\\u0026params)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Wrap failed: %s\\\", err)\\n\\t\\t}\\n\\n\\t\\terr = shh.RequestHistoricMessages(peerID, env)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to send P2P message: %s\\\", err)\\n\\t\\t}\\n\\n\\t\\ttime.Sleep(time.Second * 5)\\n\\t}\\n}\\n\\nfunc extractIDFromEnode(s string) []byte {\\n\\tn, err := discover.ParseNode(s)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to parse enode: %s\\\", err)\\n\\t}\\n\\treturn n.ID[:]\\n}\\n\\n//模糊布卢姆增加16个随机位的布卢姆\\n//筛选，以便混淆包含的主题。\\n//它在每个会话中都具有确定性。\\n//尽管有额外的位，它将平均匹配\\n//比完整节点的Bloom过滤器少32000倍的消息。\\nfunc obfuscateBloom(bloom []byte) {\\n\\tconst half = entropySize / 2\\n\\tfor i := 0; i \\u003c half; i++ {\\n\\t\\tx := int(entropy[i])\\n\\t\\tif entropy[half+i] \\u003c 128 {\\n\\t\\t\\tx += 256\\n\\t\\t}\\n\\nbloom[x/8] = 1 \\u003c\\u003c uint(x%8) //设置位号x\\n\\t}\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func main() {\",\"lineNumber\":116,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/yinchengtsinghua/go-ethereum-chinese\"},\"file\":{\"path\":\"cmd/wnode/main.go\",\"url\":\"/github.com/yinchengtsinghua/go-ethereum-chinese/-/blob/cmd/wnode/main.go\",\"content\":\"\\n//此源码被清华学神尹成大魔王专业翻译分析并修改\\n//尹成QQ77025077\\n//尹成微信18510341407\\n//尹成所在QQ群721929980\\n//尹成邮箱 yinc13@mails.tsinghua.edu.cn\\n//尹成毕业于清华大学,微软区块链领域全球最有价值专家\\n//https://mvp.microsoft.com/zh-cn/PublicProfile/4033620\\n//版权所有2017 Go Ethereum作者\\n//此文件是Go以太坊的一部分。\\n//\\n//Go以太坊是免费软件：您可以重新发布和/或修改它\\n//根据GNU通用公共许可证的条款\\n//自由软件基金会，或者许可证的第3版，或者\\n//（由您选择）任何更高版本。\\n//\\n//Go以太坊的分布希望它会有用，\\n//但没有任何保证；甚至没有\\n//适销性或特定用途的适用性。见\\n//GNU通用公共许可证了解更多详细信息。\\n//\\n//你应该已经收到一份GNU通用公共许可证的副本\\n//一起去以太坊吧。如果没有，请参见\\u003chttp://www.gnu.org/licenses/\\u003e。\\n\\n//这是一个简单的耳语节点。它可以用作独立的引导节点。\\n//也可用于不同的测试和诊断目的。\\n\\npackage main\\n\\nimport (\\n\\t\\\"bufio\\\"\\n\\t\\\"crypto/ecdsa\\\"\\n\\tcrand \\\"crypto/rand\\\"\\n\\t\\\"crypto/sha512\\\"\\n\\t\\\"encoding/binary\\\"\\n\\t\\\"encoding/hex\\\"\\n\\t\\\"flag\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"os\\\"\\n\\t\\\"path/filepath\\\"\\n\\t\\\"strconv\\\"\\n\\t\\\"strings\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/ethereum/go-ethereum/cmd/utils\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/common\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/console\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/crypto\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/log\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/p2p\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/p2p/enode\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/p2p/nat\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/whisper/mailserver\\\"\\n\\twhisper \\\"github.com/ethereum/go-ethereum/whisper/whisperv6\\\"\\n\\t\\\"golang.org/x/crypto/pbkdf2\\\"\\n)\\n\\nconst quitCommand = \\\"~Q\\\"\\nconst entropySize = 32\\n\\n//独生子女\\nvar (\\n\\tserver     *p2p.Server\\n\\tshh        *whisper.Whisper\\n\\tdone       chan struct{}\\n\\tmailServer mailserver.WMailServer\\n\\tentropy    [entropySize]byte\\n\\n\\tinput = bufio.NewReader(os.Stdin)\\n)\\n\\n//加密\\nvar (\\n\\tsymKey  []byte\\n\\tpub     *ecdsa.PublicKey\\n\\tasymKey *ecdsa.PrivateKey\\n\\tnodeid  *ecdsa.PrivateKey\\n\\ttopic   whisper.TopicType\\n\\n\\tasymKeyID    string\\n\\tasymFilterID string\\n\\tsymFilterID  string\\n\\tsymPass      string\\n\\tmsPassword   string\\n)\\n\\n//CMD参数\\nvar (\\n\\tbootstrapMode  = flag.Bool(\\\"standalone\\\", false, \\\"boostrap node: don't initiate connection to peers, just wait for incoming connections\\\")\\n\\tforwarderMode  = flag.Bool(\\\"forwarder\\\", false, \\\"forwarder mode: only forward messages, neither encrypt nor decrypt messages\\\")\\n\\tmailServerMode = flag.Bool(\\\"mailserver\\\", false, \\\"mail server mode: delivers expired messages on demand\\\")\\n\\trequestMail    = flag.Bool(\\\"mailclient\\\", false, \\\"request expired messages from the bootstrap server\\\")\\n\\tasymmetricMode = flag.Bool(\\\"asym\\\", false, \\\"use asymmetric encryption\\\")\\n\\tgenerateKey    = flag.Bool(\\\"generatekey\\\", false, \\\"generate and show the private key\\\")\\n\\tfileExMode     = flag.Bool(\\\"fileexchange\\\", false, \\\"file exchange mode\\\")\\n\\tfileReader     = flag.Bool(\\\"filereader\\\", false, \\\"load and decrypt messages saved as files, display as plain text\\\")\\n\\ttestMode       = flag.Bool(\\\"test\\\", false, \\\"use of predefined parameters for diagnostics (password, etc.)\\\")\\n\\techoMode       = flag.Bool(\\\"echo\\\", false, \\\"echo mode: prints some arguments for diagnostics\\\")\\n\\n\\targVerbosity = flag.Int(\\\"verbosity\\\", int(log.LvlError), \\\"log verbosity level\\\")\\n\\targTTL       = flag.Uint(\\\"ttl\\\", 30, \\\"time-to-live for messages in seconds\\\")\\n\\targWorkTime  = flag.Uint(\\\"work\\\", 5, \\\"work time in seconds\\\")\\n\\targMaxSize   = flag.Uint(\\\"maxsize\\\", uint(whisper.DefaultMaxMessageSize), \\\"max size of message\\\")\\n\\targPoW       = flag.Float64(\\\"pow\\\", whisper.DefaultMinimumPoW, \\\"PoW for normal messages in float format (e.g. 2.7)\\\")\\n\\targServerPoW = flag.Float64(\\\"mspow\\\", whisper.DefaultMinimumPoW, \\\"PoW requirement for Mail Server request\\\")\\n\\n\\targIP      = flag.String(\\\"ip\\\", \\\"\\\", \\\"IP address and port of this node (e.g. 127.0.0.1:30303)\\\")\\n\\targPub     = flag.String(\\\"pub\\\", \\\"\\\", \\\"public key for asymmetric encryption\\\")\\n\\targDBPath  = flag.String(\\\"dbpath\\\", \\\"\\\", \\\"path to the server's DB directory\\\")\\n\\targIDFile  = flag.String(\\\"idfile\\\", \\\"\\\", \\\"file name with node id (private key)\\\")\\nargEnode   = flag.String(\\\"boot\\\", \\\"\\\", \\\"bootstrap node you want to connect to (e.g. enode://E454……08D50@52.176.211.200:16428）”）\\n\\targTopic   = flag.String(\\\"topic\\\", \\\"\\\", \\\"topic in hexadecimal format (e.g. 70a4beef)\\\")\\n\\targSaveDir = flag.String(\\\"savedir\\\", \\\"\\\", \\\"directory where all incoming messages will be saved as files\\\")\\n)\\n\\nfunc main() {\\n\\tprocessArgs()\\n\\tinitialize()\\n\\trun()\\n\\tshutdown()\\n}\\n\\nfunc processArgs() {\\n\\tflag.Parse()\\n\\n\\tif len(*argIDFile) \\u003e 0 {\\n\\t\\tvar err error\\n\\t\\tnodeid, err = crypto.LoadECDSA(*argIDFile)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to load file [%s]: %s.\\\", *argIDFile, err)\\n\\t\\t}\\n\\t}\\n\\nconst enodePrefix = \\\"enode://“\\n\\tif len(*argEnode) \\u003e 0 {\\n\\t\\tif (*argEnode)[:len(enodePrefix)] != enodePrefix {\\n\\t\\t\\t*argEnode = enodePrefix + *argEnode\\n\\t\\t}\\n\\t}\\n\\n\\tif len(*argTopic) \\u003e 0 {\\n\\t\\tx, err := hex.DecodeString(*argTopic)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to parse the topic: %s\\\", err)\\n\\t\\t}\\n\\t\\ttopic = whisper.BytesToTopic(x)\\n\\t}\\n\\n\\tif *asymmetricMode \\u0026\\u0026 len(*argPub) \\u003e 0 {\\n\\t\\tvar err error\\n\\t\\tif pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"invalid public key\\\")\\n\\t\\t}\\n\\t}\\n\\n\\tif len(*argSaveDir) \\u003e 0 {\\n\\t\\tif _, err := os.Stat(*argSaveDir); os.IsNotExist(err) {\\n\\t\\t\\tutils.Fatalf(\\\"Download directory '%s' does not exist\\\", *argSaveDir)\\n\\t\\t}\\n\\t} else if *fileExMode {\\n\\t\\tutils.Fatalf(\\\"Parameter 'savedir' is mandatory for file exchange mode\\\")\\n\\t}\\n\\n\\tif *echoMode {\\n\\t\\techo()\\n\\t}\\n}\\n\\nfunc echo() {\\n\\tfmt.Printf(\\\"ttl = %d \\\\n\\\", *argTTL)\\n\\tfmt.Printf(\\\"workTime = %d \\\\n\\\", *argWorkTime)\\n\\tfmt.Printf(\\\"pow = %f \\\\n\\\", *argPoW)\\n\\tfmt.Printf(\\\"mspow = %f \\\\n\\\", *argServerPoW)\\n\\tfmt.Printf(\\\"ip = %s \\\\n\\\", *argIP)\\n\\tfmt.Printf(\\\"pub = %s \\\\n\\\", common.ToHex(crypto.FromECDSAPub(pub)))\\n\\tfmt.Printf(\\\"idfile = %s \\\\n\\\", *argIDFile)\\n\\tfmt.Printf(\\\"dbpath = %s \\\\n\\\", *argDBPath)\\n\\tfmt.Printf(\\\"boot = %s \\\\n\\\", *argEnode)\\n}\\n\\nfunc initialize() {\\n\\tlog.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))\\n\\n\\tdone = make(chan struct{})\\n\\tvar peers []*enode.Node\\n\\tvar err error\\n\\n\\tif *generateKey {\\n\\t\\tkey, err := crypto.GenerateKey()\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to generate private key: %s\\\", err)\\n\\t\\t}\\n\\t\\tk := hex.EncodeToString(crypto.FromECDSA(key))\\n\\t\\tfmt.Printf(\\\"Random private key: %s \\\\n\\\", k)\\n\\t\\tos.Exit(0)\\n\\t}\\n\\n\\tif *testMode {\\nsymPass = \\\"wwww\\\" //ASCII码：0x77777777\\n\\t\\tmsPassword = \\\"wwww\\\"\\n\\t}\\n\\n\\tif *bootstrapMode {\\n\\t\\tif len(*argIP) == 0 {\\n\\t\\t\\targIP = scanLineA(\\\"Please enter your IP and port (e.g. 127.0.0.1:30348): \\\")\\n\\t\\t}\\n\\t} else if *fileReader {\\n\\t\\t*bootstrapMode = true\\n\\t} else {\\n\\t\\tif len(*argEnode) == 0 {\\n\\t\\t\\targEnode = scanLineA(\\\"Please enter the peer's enode: \\\")\\n\\t\\t}\\n\\t\\tpeer := enode.MustParseV4(*argEnode)\\n\\t\\tpeers = append(peers, peer)\\n\\t}\\n\\n\\tif *mailServerMode {\\n\\t\\tif len(msPassword) == 0 {\\n\\t\\t\\tmsPassword, err = console.Stdin.PromptPassword(\\\"Please enter the Mail Server password: \\\")\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Failed to read Mail Server password: %s\\\", err)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\tcfg := \\u0026whisper.Config{\\n\\t\\tMaxMessageSize:     uint32(*argMaxSize),\\n\\t\\tMinimumAcceptedPOW: *argPoW,\\n\\t}\\n\\n\\tshh = whisper.New(cfg)\\n\\n\\tif *argPoW != whisper.DefaultMinimumPoW {\\n\\t\\terr := shh.SetMinimumPoW(*argPoW)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to set PoW: %s\\\", err)\\n\\t\\t}\\n\\t}\\n\\n\\tif uint32(*argMaxSize) != whisper.DefaultMaxMessageSize {\\n\\t\\terr := shh.SetMaxMessageSize(uint32(*argMaxSize))\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to set max message size: %s\\\", err)\\n\\t\\t}\\n\\t}\\n\\n\\tasymKeyID, err = shh.NewKeyPair()\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to generate a new key pair: %s\\\", err)\\n\\t}\\n\\n\\tasymKey, err = shh.GetPrivateKey(asymKeyID)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to retrieve a new key pair: %s\\\", err)\\n\\t}\\n\\n\\tif nodeid == nil {\\n\\t\\ttmpID, err := shh.NewKeyPair()\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to generate a new key pair: %s\\\", err)\\n\\t\\t}\\n\\n\\t\\tnodeid, err = shh.GetPrivateKey(tmpID)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to retrieve a new key pair: %s\\\", err)\\n\\t\\t}\\n\\t}\\n\\n\\tmaxPeers := 80\\n\\tif *bootstrapMode {\\n\\t\\tmaxPeers = 800\\n\\t}\\n\\n\\t_, err = crand.Read(entropy[:])\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"crypto/rand failed: %s\\\", err)\\n\\t}\\n\\n\\tif *mailServerMode {\\n\\t\\tshh.RegisterServer(\\u0026mailServer)\\n\\t\\tif err := mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW); err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to init MailServer: %s\\\", err)\\n\\t\\t}\\n\\t}\\n\\n\\tserver = \\u0026p2p.Server{\\n\\t\\tConfig: p2p.Config{\\n\\t\\t\\tPrivateKey:     nodeid,\\n\\t\\t\\tMaxPeers:       maxPeers,\\n\\t\\t\\tName:           common.MakeName(\\\"wnode\\\", \\\"6.0\\\"),\\n\\t\\t\\tProtocols:      shh.Protocols(),\\n\\t\\t\\tListenAddr:     *argIP,\\n\\t\\t\\tNAT:            nat.Any(),\\n\\t\\t\\tBootstrapNodes: peers,\\n\\t\\t\\tStaticNodes:    peers,\\n\\t\\t\\tTrustedNodes:   peers,\\n\\t\\t},\\n\\t}\\n}\\n\\nfunc startServer() error {\\n\\terr := server.Start()\\n\\tif err != nil {\\n\\t\\tfmt.Printf(\\\"Failed to start Whisper peer: %s.\\\", err)\\n\\t\\treturn err\\n\\t}\\n\\n\\tfmt.Printf(\\\"my public key: %s \\\\n\\\", common.ToHex(crypto.FromECDSAPub(\\u0026asymKey.PublicKey)))\\n\\tfmt.Println(server.NodeInfo().Enode)\\n\\n\\tif *bootstrapMode {\\n\\t\\tconfigureNode()\\n\\t\\tfmt.Println(\\\"Bootstrap Whisper node started\\\")\\n\\t} else {\\n\\t\\tfmt.Println(\\\"Whisper node started\\\")\\n//首先看看我们是否可以建立连接，然后请求用户输入\\n\\t\\twaitForConnection(true)\\n\\t\\tconfigureNode()\\n\\t}\\n\\n\\tif *fileExMode {\\n\\t\\tfmt.Printf(\\\"Please type the file name to be send. To quit type: '%s'\\\\n\\\", quitCommand)\\n\\t} else if *fileReader {\\n\\t\\tfmt.Printf(\\\"Please type the file name to be decrypted. To quit type: '%s'\\\\n\\\", quitCommand)\\n\\t} else if !*forwarderMode {\\n\\t\\tfmt.Printf(\\\"Please type the message. To quit type: '%s'\\\\n\\\", quitCommand)\\n\\t}\\n\\treturn nil\\n}\\n\\nfunc configureNode() {\\n\\tvar err error\\n\\tvar p2pAccept bool\\n\\n\\tif *forwarderMode {\\n\\t\\treturn\\n\\t}\\n\\n\\tif *asymmetricMode {\\n\\t\\tif len(*argPub) == 0 {\\n\\t\\t\\ts := scanLine(\\\"Please enter the peer's public key: \\\")\\n\\t\\t\\tb := common.FromHex(s)\\n\\t\\t\\tif b == nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Error: can not convert hexadecimal string\\\")\\n\\t\\t\\t}\\n\\t\\t\\tif pub, err = crypto.UnmarshalPubkey(b); err != nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Error: invalid peer public key\\\")\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\tif *requestMail {\\n\\t\\tp2pAccept = true\\n\\t\\tif len(msPassword) == 0 {\\n\\t\\t\\tmsPassword, err = console.Stdin.PromptPassword(\\\"Please enter the Mail Server password: \\\")\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Failed to read Mail Server password: %s\\\", err)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\tif !*asymmetricMode \\u0026\\u0026 !*forwarderMode {\\n\\t\\tif len(symPass) == 0 {\\n\\t\\t\\tsymPass, err = console.Stdin.PromptPassword(\\\"Please enter the password for symmetric encryption: \\\")\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Failed to read passphrase: %v\\\", err)\\n\\t\\t\\t}\\n\\t\\t}\\n\\n\\t\\tsymKeyID, err := shh.AddSymKeyFromPassword(symPass)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to create symmetric key: %s\\\", err)\\n\\t\\t}\\n\\t\\tsymKey, err = shh.GetSymKey(symKeyID)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to save symmetric key: %s\\\", err)\\n\\t\\t}\\n\\t\\tif len(*argTopic) == 0 {\\n\\t\\t\\tgenerateTopic([]byte(symPass))\\n\\t\\t}\\n\\n\\t\\tfmt.Printf(\\\"Filter is configured for the topic: %x \\\\n\\\", topic)\\n\\t}\\n\\n\\tif *mailServerMode {\\n\\t\\tif len(*argDBPath) == 0 {\\n\\t\\t\\targDBPath = scanLineA(\\\"Please enter the path to DB file: \\\")\\n\\t\\t}\\n\\t}\\n\\n\\tsymFilter := whisper.Filter{\\n\\t\\tKeySym:   symKey,\\n\\t\\tTopics:   [][]byte{topic[:]},\\n\\t\\tAllowP2P: p2pAccept,\\n\\t}\\n\\tsymFilterID, err = shh.Subscribe(\\u0026symFilter)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to install filter: %s\\\", err)\\n\\t}\\n\\n\\tasymFilter := whisper.Filter{\\n\\t\\tKeyAsym:  asymKey,\\n\\t\\tTopics:   [][]byte{topic[:]},\\n\\t\\tAllowP2P: p2pAccept,\\n\\t}\\n\\tasymFilterID, err = shh.Subscribe(\\u0026asymFilter)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to install filter: %s\\\", err)\\n\\t}\\n}\\n\\nfunc generateTopic(password []byte) {\\n\\tx := pbkdf2.Key(password, password, 4096, 128, sha512.New)\\n\\tfor i := 0; i \\u003c len(x); i++ {\\n\\t\\ttopic[i%whisper.TopicLength] ^= x[i]\\n\\t}\\n}\\n\\nfunc waitForConnection(timeout bool) {\\n\\tvar cnt int\\n\\tvar connected bool\\n\\tfor !connected {\\n\\t\\ttime.Sleep(time.Millisecond * 50)\\n\\t\\tconnected = server.PeerCount() \\u003e 0\\n\\t\\tif timeout {\\n\\t\\t\\tcnt++\\n\\t\\t\\tif cnt \\u003e 1000 {\\n\\t\\t\\t\\tutils.Fatalf(\\\"Timeout expired, failed to connect\\\")\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\tfmt.Println(\\\"Connected to peer.\\\")\\n}\\n\\nfunc run() {\\n\\terr := startServer()\\n\\tif err != nil {\\n\\t\\treturn\\n\\t}\\n\\tdefer server.Stop()\\n\\tshh.Start(nil)\\n\\tdefer shh.Stop()\\n\\n\\tif !*forwarderMode {\\n\\t\\tgo messageLoop()\\n\\t}\\n\\n\\tif *requestMail {\\n\\t\\trequestExpiredMessagesLoop()\\n\\t} else if *fileExMode {\\n\\t\\tsendFilesLoop()\\n\\t} else if *fileReader {\\n\\t\\tfileReaderLoop()\\n\\t} else {\\n\\t\\tsendLoop()\\n\\t}\\n}\\n\\nfunc shutdown() {\\n\\tclose(done)\\n\\tmailServer.Close()\\n}\\n\\nfunc sendLoop() {\\n\\tfor {\\n\\t\\ts := scanLine(\\\"\\\")\\n\\t\\tif s == quitCommand {\\n\\t\\t\\tfmt.Println(\\\"Quit command received\\\")\\n\\t\\t\\treturn\\n\\t\\t}\\n\\t\\tsendMsg([]byte(s))\\n\\t\\tif *asymmetricMode {\\n//为方便起见，请打印您自己的消息，\\n//因为在非对称模式下是不可能解密的\\n\\t\\t\\ttimestamp := time.Now().Unix()\\n\\t\\t\\tfrom := crypto.PubkeyToAddress(asymKey.PublicKey)\\n\\t\\t\\tfmt.Printf(\\\"\\\\n%d \\u003c%x\\u003e: %s\\\\n\\\", timestamp, from, s)\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc sendFilesLoop() {\\n\\tfor {\\n\\t\\ts := scanLine(\\\"\\\")\\n\\t\\tif s == quitCommand {\\n\\t\\t\\tfmt.Println(\\\"Quit command received\\\")\\n\\t\\t\\treturn\\n\\t\\t}\\n\\t\\tb, err := ioutil.ReadFile(s)\\n\\t\\tif err != nil {\\n\\t\\t\\tfmt.Printf(\\\"\\u003e\\u003e\\u003e Error: %s \\\\n\\\", err)\\n\\t\\t} else {\\n\\t\\t\\th := sendMsg(b)\\n\\t\\t\\tif (h == common.Hash{}) {\\n\\t\\t\\t\\tfmt.Printf(\\\"\\u003e\\u003e\\u003e Error: message was not sent \\\\n\\\")\\n\\t\\t\\t} else {\\n\\t\\t\\t\\ttimestamp := time.Now().Unix()\\n\\t\\t\\t\\tfrom := crypto.PubkeyToAddress(asymKey.PublicKey)\\n\\t\\t\\t\\tfmt.Printf(\\\"\\\\n%d \\u003c%x\\u003e: sent message with hash %x\\\\n\\\", timestamp, from, h)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc fileReaderLoop() {\\n\\twatcher1 := shh.GetFilter(symFilterID)\\n\\twatcher2 := shh.GetFilter(asymFilterID)\\n\\tif watcher1 == nil \\u0026\\u0026 watcher2 == nil {\\n\\t\\tfmt.Println(\\\"Error: neither symmetric nor asymmetric filter is installed\\\")\\n\\t\\treturn\\n\\t}\\n\\n\\tfor {\\n\\t\\ts := scanLine(\\\"\\\")\\n\\t\\tif s == quitCommand {\\n\\t\\t\\tfmt.Println(\\\"Quit command received\\\")\\n\\t\\t\\treturn\\n\\t\\t}\\n\\t\\traw, err := ioutil.ReadFile(s)\\n\\t\\tif err != nil {\\n\\t\\t\\tfmt.Printf(\\\"\\u003e\\u003e\\u003e Error: %s \\\\n\\\", err)\\n\\t\\t} else {\\nenv := whisper.Envelope{Data: raw} //主题为零\\nmsg := env.Open(watcher1)          //不管主题如何，都强制打开信封\\n\\t\\t\\tif msg == nil {\\n\\t\\t\\t\\tmsg = env.Open(watcher2)\\n\\t\\t\\t}\\n\\t\\t\\tif msg == nil {\\n\\t\\t\\t\\tfmt.Printf(\\\"\\u003e\\u003e\\u003e Error: failed to decrypt the message \\\\n\\\")\\n\\t\\t\\t} else {\\n\\t\\t\\t\\tprintMessageInfo(msg)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc scanLine(prompt string) string {\\n\\tif len(prompt) \\u003e 0 {\\n\\t\\tfmt.Print(prompt)\\n\\t}\\n\\ttxt, err := input.ReadString('\\\\n')\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"input error: %s\\\", err)\\n\\t}\\n\\ttxt = strings.TrimRight(txt, \\\"\\\\n\\\\r\\\")\\n\\treturn txt\\n}\\n\\nfunc scanLineA(prompt string) *string {\\n\\ts := scanLine(prompt)\\n\\treturn \\u0026s\\n}\\n\\nfunc scanUint(prompt string) uint32 {\\n\\ts := scanLine(prompt)\\n\\ti, err := strconv.Atoi(s)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Fail to parse the lower time limit: %s\\\", err)\\n\\t}\\n\\treturn uint32(i)\\n}\\n\\nfunc sendMsg(payload []byte) common.Hash {\\n\\tparams := whisper.MessageParams{\\n\\t\\tSrc:      asymKey,\\n\\t\\tDst:      pub,\\n\\t\\tKeySym:   symKey,\\n\\t\\tPayload:  payload,\\n\\t\\tTopic:    topic,\\n\\t\\tTTL:      uint32(*argTTL),\\n\\t\\tPoW:      *argPoW,\\n\\t\\tWorkTime: uint32(*argWorkTime),\\n\\t}\\n\\n\\tmsg, err := whisper.NewSentMessage(\\u0026params)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"failed to create new message: %s\\\", err)\\n\\t}\\n\\n\\tenvelope, err := msg.Wrap(\\u0026params)\\n\\tif err != nil {\\n\\t\\tfmt.Printf(\\\"failed to seal message: %v \\\\n\\\", err)\\n\\t\\treturn common.Hash{}\\n\\t}\\n\\n\\terr = shh.Send(envelope)\\n\\tif err != nil {\\n\\t\\tfmt.Printf(\\\"failed to send message: %v \\\\n\\\", err)\\n\\t\\treturn common.Hash{}\\n\\t}\\n\\n\\treturn envelope.Hash()\\n}\\n\\nfunc messageLoop() {\\n\\tsf := shh.GetFilter(symFilterID)\\n\\tif sf == nil {\\n\\t\\tutils.Fatalf(\\\"symmetric filter is not installed\\\")\\n\\t}\\n\\n\\taf := shh.GetFilter(asymFilterID)\\n\\tif af == nil {\\n\\t\\tutils.Fatalf(\\\"asymmetric filter is not installed\\\")\\n\\t}\\n\\n\\tticker := time.NewTicker(time.Millisecond * 50)\\n\\n\\tfor {\\n\\t\\tselect {\\n\\t\\tcase \\u003c-ticker.C:\\n\\t\\t\\tm1 := sf.Retrieve()\\n\\t\\t\\tm2 := af.Retrieve()\\n\\t\\t\\tmessages := append(m1, m2...)\\n\\t\\t\\tfor _, msg := range messages {\\n\\t\\t\\t\\treportedOnce := false\\n\\t\\t\\t\\tif !*fileExMode \\u0026\\u0026 len(msg.Payload) \\u003c= 2048 {\\n\\t\\t\\t\\t\\tprintMessageInfo(msg)\\n\\t\\t\\t\\t\\treportedOnce = true\\n\\t\\t\\t\\t}\\n\\n//在指定argsavedir时保存所有消息。\\n//FileExMode仅指定消息保存后如何显示在控制台上。\\n//如果fileexmode==true，则只显示哈希值，因为消息可能太大。\\n\\t\\t\\t\\tif len(*argSaveDir) \\u003e 0 {\\n\\t\\t\\t\\t\\twriteMessageToFile(*argSaveDir, msg, !reportedOnce)\\n\\t\\t\\t\\t}\\n\\t\\t\\t}\\n\\t\\tcase \\u003c-done:\\n\\t\\t\\treturn\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc printMessageInfo(msg *whisper.ReceivedMessage) {\\ntimestamp := fmt.Sprintf(\\\"%d\\\", msg.Sent) //用于诊断的Unix时间戳\\n\\ttext := string(msg.Payload)\\n\\n\\tvar address common.Address\\n\\tif msg.Src != nil {\\n\\t\\taddress = crypto.PubkeyToAddress(*msg.Src)\\n\\t}\\n\\n\\tif whisper.IsPubKeyEqual(msg.Src, \\u0026asymKey.PublicKey) {\\nfmt.Printf(\\\"\\\\n%s \\u003c%x\\u003e: %s\\\\n\\\", timestamp, address, text) //我的信息\\n\\t} else {\\nfmt.Printf(\\\"\\\\n%s [%x]: %s\\\\n\\\", timestamp, address, text) //来自对等方的消息\\n\\t}\\n}\\n\\nfunc writeMessageToFile(dir string, msg *whisper.ReceivedMessage, show bool) {\\n\\tif len(dir) == 0 {\\n\\t\\treturn\\n\\t}\\n\\n\\ttimestamp := fmt.Sprintf(\\\"%d\\\", msg.Sent)\\n\\tname := fmt.Sprintf(\\\"%x\\\", msg.EnvelopeHash)\\n\\n\\tvar address common.Address\\n\\tif msg.Src != nil {\\n\\t\\taddress = crypto.PubkeyToAddress(*msg.Src)\\n\\t}\\n\\n\\tenv := shh.GetEnvelope(msg.EnvelopeHash)\\n\\tif env == nil {\\n\\t\\tfmt.Printf(\\\"\\\\nUnexpected error: envelope not found: %x\\\\n\\\", msg.EnvelopeHash)\\n\\t\\treturn\\n\\t}\\n\\n//这是一个示例代码；如果不想保存自己的消息，请取消注释。\\n//如果whisper.ispubkeyequal（msg.src，\\u0026asymkey.publickey）\\n//fmt.printf（“\\\\n%s\\u003c%x\\u003e：我收到的消息，未保存：“%s'\\\\n”，时间戳，地址，名称）\\n//返回\\n//}\\n\\n\\tfullpath := filepath.Join(dir, name)\\n\\terr := ioutil.WriteFile(fullpath, env.Data, 0644)\\n\\tif err != nil {\\n\\t\\tfmt.Printf(\\\"\\\\n%s {%x}: message received but not saved: %s\\\\n\\\", timestamp, address, err)\\n\\t} else if show {\\n\\t\\tfmt.Printf(\\\"\\\\n%s {%x}: message received and saved as '%s' (%d bytes)\\\\n\\\", timestamp, address, name, len(env.Data))\\n\\t}\\n}\\n\\nfunc requestExpiredMessagesLoop() {\\n\\tvar key, peerID, bloom []byte\\n\\tvar timeLow, timeUpp uint32\\n\\tvar t string\\n\\tvar xt whisper.TopicType\\n\\n\\tkeyID, err := shh.AddSymKeyFromPassword(msPassword)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to create symmetric key for mail request: %s\\\", err)\\n\\t}\\n\\tkey, err = shh.GetSymKey(keyID)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to save symmetric key for mail request: %s\\\", err)\\n\\t}\\n\\tpeerID = extractIDFromEnode(*argEnode)\\n\\tshh.AllowP2PMessagesFromPeer(peerID)\\n\\n\\tfor {\\n\\t\\ttimeLow = scanUint(\\\"Please enter the lower limit of the time range (unix timestamp): \\\")\\n\\t\\ttimeUpp = scanUint(\\\"Please enter the upper limit of the time range (unix timestamp): \\\")\\n\\t\\tt = scanLine(\\\"Enter the topic (hex). Press enter to request all messages, regardless of the topic: \\\")\\n\\t\\tif len(t) == whisper.TopicLength*2 {\\n\\t\\t\\tx, err := hex.DecodeString(t)\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\tfmt.Printf(\\\"Failed to parse the topic: %s \\\\n\\\", err)\\n\\t\\t\\t\\tcontinue\\n\\t\\t\\t}\\n\\t\\t\\txt = whisper.BytesToTopic(x)\\n\\t\\t\\tbloom = whisper.TopicToBloom(xt)\\n\\t\\t\\tobfuscateBloom(bloom)\\n\\t\\t} else if len(t) == 0 {\\n\\t\\t\\tbloom = whisper.MakeFullNodeBloom()\\n\\t\\t} else {\\n\\t\\t\\tfmt.Println(\\\"Error: topic is invalid, request aborted\\\")\\n\\t\\t\\tcontinue\\n\\t\\t}\\n\\n\\t\\tif timeUpp == 0 {\\n\\t\\t\\ttimeUpp = 0xFFFFFFFF\\n\\t\\t}\\n\\n\\t\\tdata := make([]byte, 8, 8+whisper.BloomFilterSize)\\n\\t\\tbinary.BigEndian.PutUint32(data, timeLow)\\n\\t\\tbinary.BigEndian.PutUint32(data[4:], timeUpp)\\n\\t\\tdata = append(data, bloom...)\\n\\n\\t\\tvar params whisper.MessageParams\\n\\t\\tparams.PoW = *argServerPoW\\n\\t\\tparams.Payload = data\\n\\t\\tparams.KeySym = key\\n\\t\\tparams.Src = asymKey\\n\\t\\tparams.WorkTime = 5\\n\\n\\t\\tmsg, err := whisper.NewSentMessage(\\u0026params)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"failed to create new message: %s\\\", err)\\n\\t\\t}\\n\\t\\tenv, err := msg.Wrap(\\u0026params)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Wrap failed: %s\\\", err)\\n\\t\\t}\\n\\n\\t\\terr = shh.RequestHistoricMessages(peerID, env)\\n\\t\\tif err != nil {\\n\\t\\t\\tutils.Fatalf(\\\"Failed to send P2P message: %s\\\", err)\\n\\t\\t}\\n\\n\\t\\ttime.Sleep(time.Second * 5)\\n\\t}\\n}\\n\\nfunc extractIDFromEnode(s string) []byte {\\n\\tn, err := enode.ParseV4(s)\\n\\tif err != nil {\\n\\t\\tutils.Fatalf(\\\"Failed to parse enode: %s\\\", err)\\n\\t}\\n\\treturn n.ID().Bytes()\\n}\\n\\n//模糊布卢姆增加16个随机位到布卢姆\\n//筛选，以便混淆包含的主题。\\n//它在每个会话中都具有确定性。\\n//尽管有额外的位，它将平均匹配\\n//比完整节点的Bloom过滤器少32000倍的消息。\\nfunc obfuscateBloom(bloom []byte) {\\n\\tconst half = entropySize / 2\\n\\tfor i := 0; i \\u003c half; i++ {\\n\\t\\tx := int(entropy[i])\\n\\t\\tif entropy[half+i] \\u003c 128 {\\n\\t\\t\\tx += 256\\n\\t\\t}\\n\\nbloom[x/8] = 1 \\u003c\\u003c uint(x%8) //设置位号x\\n\\t}\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func main() {\",\"lineNumber\":116,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/netobserv/netobserv-ebpf-agent\"},\"file\":{\"path\":\"vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"url\":\"/github.com/netobserv/netobserv-ebpf-agent/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"content\":\"// Copyright 2017 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//    http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\npackage gnostic_extension_v1\\n\\nimport (\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"google.golang.org/protobuf/proto\\\"\\n\\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n)\\n\\ntype extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n\\n// Main implements the main program of an extension handler.\\nfunc Main(handler extensionHandler) {\\n\\t// unpack the request\\n\\tdata, err := ioutil.ReadAll(os.Stdin)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\tif len(data) == 0 {\\n\\t\\tlog.Println(\\\"No input data.\\\")\\n\\t\\tos.Exit(1)\\n\\t}\\n\\trequest := \\u0026ExtensionHandlerRequest{}\\n\\terr = proto.Unmarshal(data, request)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Input error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\t// call the handler\\n\\thandled, output, err := handler(request.Wrapper.ExtensionName, request.Wrapper.Yaml)\\n\\t// respond with the output of the handler\\n\\tresponse := \\u0026ExtensionHandlerResponse{\\n\\t\\tHandled: false, // default assumption\\n\\t\\tErrors:  make([]string, 0),\\n\\t}\\n\\tif err != nil {\\n\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t} else if handled {\\n\\t\\tresponse.Handled = true\\n\\t\\tresponse.Value, err = anypb.New(output)\\n\\t\\tif err != nil {\\n\\t\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t\\t}\\n\\t}\\n\\tresponseBytes, _ := proto.Marshal(response)\\n\\tos.Stdout.Write(responseBytes)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(handler extensionHandler) {\",\"lineNumber\":28,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/openshift/cluster-network-operator\"},\"file\":{\"path\":\"vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"url\":\"/github.com/openshift/cluster-network-operator/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"content\":\"// Copyright 2017 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//    http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\npackage gnostic_extension_v1\\n\\nimport (\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"google.golang.org/protobuf/proto\\\"\\n\\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n)\\n\\ntype extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n\\n// Main implements the main program of an extension handler.\\nfunc Main(handler extensionHandler) {\\n\\t// unpack the request\\n\\tdata, err := ioutil.ReadAll(os.Stdin)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\tif len(data) == 0 {\\n\\t\\tlog.Println(\\\"No input data.\\\")\\n\\t\\tos.Exit(1)\\n\\t}\\n\\trequest := \\u0026ExtensionHandlerRequest{}\\n\\terr = proto.Unmarshal(data, request)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Input error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\t// call the handler\\n\\thandled, output, err := handler(request.Wrapper.ExtensionName, request.Wrapper.Yaml)\\n\\t// respond with the output of the handler\\n\\tresponse := \\u0026ExtensionHandlerResponse{\\n\\t\\tHandled: false, // default assumption\\n\\t\\tErrors:  make([]string, 0),\\n\\t}\\n\\tif err != nil {\\n\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t} else if handled {\\n\\t\\tresponse.Handled = true\\n\\t\\tresponse.Value, err = anypb.New(output)\\n\\t\\tif err != nil {\\n\\t\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t\\t}\\n\\t}\\n\\tresponseBytes, _ := proto.Marshal(response)\\n\\tos.Stdout.Write(responseBytes)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(handler extensionHandler) {\",\"lineNumber\":28,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/tektoncd/hub\"},\"file\":{\"path\":\"vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"url\":\"/github.com/tektoncd/hub/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"content\":\"// Copyright 2017 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//    http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\npackage gnostic_extension_v1\\n\\nimport (\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"google.golang.org/protobuf/proto\\\"\\n\\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n)\\n\\ntype extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n\\n// Main implements the main program of an extension handler.\\nfunc Main(handler extensionHandler) {\\n\\t// unpack the request\\n\\tdata, err := ioutil.ReadAll(os.Stdin)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\tif len(data) == 0 {\\n\\t\\tlog.Println(\\\"No input data.\\\")\\n\\t\\tos.Exit(1)\\n\\t}\\n\\trequest := \\u0026ExtensionHandlerRequest{}\\n\\terr = proto.Unmarshal(data, request)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Input error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\t// call the handler\\n\\thandled, output, err := handler(request.Wrapper.ExtensionName, request.Wrapper.Yaml)\\n\\t// respond with the output of the handler\\n\\tresponse := \\u0026ExtensionHandlerResponse{\\n\\t\\tHandled: false, // default assumption\\n\\t\\tErrors:  make([]string, 0),\\n\\t}\\n\\tif err != nil {\\n\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t} else if handled {\\n\\t\\tresponse.Handled = true\\n\\t\\tresponse.Value, err = anypb.New(output)\\n\\t\\tif err != nil {\\n\\t\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t\\t}\\n\\t}\\n\\tresponseBytes, _ := proto.Marshal(response)\\n\\tos.Stdout.Write(responseBytes)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(handler extensionHandler) {\",\"lineNumber\":28,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/opencontainers/umoci\"},\"file\":{\"path\":\"cmd/umoci/main.go\",\"url\":\"/github.com/opencontainers/umoci/-/blob/cmd/umoci/main.go\",\"content\":\"// SPDX-License-Identifier: Apache-2.0\\n/*\\n * umoci: Umoci Modifies Open Containers' Images\\n * Copyright (C) 2016-2025 SUSE LLC\\n *\\n * Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n * you may not use this file except in compliance with the License.\\n * You may obtain a copy of the License at\\n *\\n *    http://www.apache.org/licenses/LICENSE-2.0\\n *\\n * Unless required by applicable law or agreed to in writing, software\\n * distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n * See the License for the specific language governing permissions and\\n * limitations under the License.\\n */\\n\\n// Package main is the cli implementation of umoci.\\npackage main\\n\\nimport (\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"os\\\"\\n\\t\\\"runtime\\\"\\n\\t\\\"runtime/pprof\\\"\\n\\n\\t\\\"github.com/apex/log\\\"\\n\\tlogcli \\\"github.com/apex/log/handlers/cli\\\"\\n\\timeta \\\"github.com/opencontainers/image-spec/specs-go\\\"\\n\\trspec \\\"github.com/opencontainers/runtime-spec/specs-go\\\"\\n\\t\\\"github.com/urfave/cli\\\"\\n\\n\\t\\\"github.com/opencontainers/umoci\\\"\\n\\t\\\"github.com/opencontainers/umoci/internal/testhelpers\\\"\\n)\\n\\nconst (\\n\\tusage = `umoci modifies Open Container images`\\n\\n\\t// Categories used to automatically monkey-patch flags to commands.\\n\\tcategoryLayout = \\\"layout\\\"\\n\\tcategoryImage  = \\\"image\\\"\\n)\\n\\nfunc printVersion(c *cli.Context) {\\n\\tw := c.App.Writer\\n\\n\\tfmt.Fprintln(w, \\\"umoci version\\\", c.App.Version) //nolint:errcheck // errors not relevant\\n\\tif testhelpers.IsTestBinary() {\\n\\t\\t// This string is matched against in some tests (namely the xattr\\n\\t\\t// masking tests) to detect whether the umoci binary can be used for\\n\\t\\t// those tests. Make sure to update umoci-is-test-binary if you change\\n\\t\\t// this string!\\n\\t\\tfmt.Fprintln(w, \\\"== THIS UMOCI BINARY IS COMPILED IN TEST MODE ==\\\") //nolint:errcheck // errors not relevant\\n\\t}\\n\\tfmt.Fprintln(w, \\\"image spec:\\\", imeta.Version)   //nolint:errcheck // errors not relevant\\n\\tfmt.Fprintln(w, \\\"runtime spec:\\\", rspec.Version) //nolint:errcheck // errors not relevant\\n\\tfmt.Fprintln(w, \\\"go:\\\", runtime.Version())       //nolint:errcheck // errors not relevant\\n}\\n\\n// Main is the underlying main() implementation. You can call this directly as\\n// though it were the command-line arguments of the umoci binary (this is\\n// needed for umoci's integration test hacks you can find in main_test.go).\\nfunc Main(args []string) error {\\n\\tapp := cli.NewApp()\\n\\tapp.Name = \\\"umoci\\\"\\n\\tapp.Usage = usage\\n\\tapp.Authors = []cli.Author{\\n\\t\\t{\\n\\t\\t\\tName:  \\\"Aleksa Sarai\\\",\\n\\t\\t\\tEmail: \\\"cyphar@cyphar.com\\\",\\n\\t\\t},\\n\\t}\\n\\n\\tapp.Version = umoci.FullVersion()\\n\\tcli.VersionPrinter = printVersion\\n\\n\\tapp.Flags = []cli.Flag{\\n\\t\\tcli.BoolFlag{\\n\\t\\t\\tName:  \\\"verbose\\\",\\n\\t\\t\\tUsage: \\\"alias for --log=info\\\",\\n\\t\\t},\\n\\t\\tcli.StringFlag{\\n\\t\\t\\tName:  \\\"log\\\",\\n\\t\\t\\tUsage: \\\"set the log level (debug, info, [warn], error, fatal)\\\",\\n\\t\\t\\tValue: \\\"warn\\\",\\n\\t\\t},\\n\\t\\tcli.StringFlag{\\n\\t\\t\\tName:   \\\"cpu-profile\\\",\\n\\t\\t\\tUsage:  \\\"profile umoci during execution and output it to a file\\\",\\n\\t\\t\\tHidden: true,\\n\\t\\t},\\n\\t}\\n\\n\\tapp.Before = func(ctx *cli.Context) error {\\n\\t\\tlog.SetHandler(logcli.New(os.Stderr))\\n\\n\\t\\tif ctx.GlobalBool(\\\"verbose\\\") {\\n\\t\\t\\tif ctx.GlobalIsSet(\\\"log\\\") {\\n\\t\\t\\t\\treturn errors.New(\\\"--log=* and --verbose are mutually exclusive\\\")\\n\\t\\t\\t}\\n\\t\\t\\tif err := ctx.GlobalSet(\\\"log\\\", \\\"info\\\"); err != nil {\\n\\t\\t\\t\\t// Should _never_ be reached.\\n\\t\\t\\t\\treturn fmt.Errorf(\\\"[internal error] failure auto-setting --log=info: %w\\\", err)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t\\tlevel, err := log.ParseLevel(ctx.GlobalString(\\\"log\\\"))\\n\\t\\tif err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"parsing log level: %w\\\", err)\\n\\t\\t}\\n\\t\\tlog.SetLevel(level)\\n\\n\\t\\tif path := ctx.GlobalString(\\\"cpu-profile\\\"); path != \\\"\\\" {\\n\\t\\t\\tfh, err := os.Create(path)\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\treturn fmt.Errorf(\\\"opening cpu-profile path: %w\\\", err)\\n\\t\\t\\t}\\n\\t\\t\\tif err := pprof.StartCPUProfile(fh); err != nil {\\n\\t\\t\\t\\treturn fmt.Errorf(\\\"start cpu-profile: %w\\\", err)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t\\treturn nil\\n\\t}\\n\\n\\tapp.After = func(*cli.Context) error {\\n\\t\\tpprof.StopCPUProfile()\\n\\t\\treturn nil\\n\\t}\\n\\n\\tapp.Commands = []cli.Command{\\n\\t\\tconfigCommand,\\n\\t\\tunpackCommand,\\n\\t\\trepackCommand,\\n\\t\\tgcCommand,\\n\\t\\tinitCommand,\\n\\t\\tnewCommand,\\n\\t\\ttagAddCommand,\\n\\t\\ttagRemoveCommand,\\n\\t\\ttagListCommand,\\n\\t\\tstatCommand,\\n\\t\\trawSubcommand,\\n\\t\\tinsertCommand,\\n\\t}\\n\\n\\tapp.Metadata = map[string]any{}\\n\\n\\t// In order to make the uxXyz wrappers not too cumbersome we automatically\\n\\t// add them to images with categories set to categoryImage or\\n\\t// categoryLayout. Monkey patching was never this neat.\\n\\tforeachSubcommand(app.Commands, func(cmd *cli.Command) {\\n\\t\\tswitch cmd.Category {\\n\\t\\tcase categoryImage:\\n\\t\\t\\toldBefore := cmd.Before\\n\\t\\t\\tcmd.Before = func(ctx *cli.Context) error {\\n\\t\\t\\t\\tif _, ok := ctx.App.Metadata[\\\"--image-path\\\"]; !ok {\\n\\t\\t\\t\\t\\treturn errors.New(\\\"missing mandatory argument: --image\\\")\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tif _, ok := ctx.App.Metadata[\\\"--image-tag\\\"]; !ok {\\n\\t\\t\\t\\t\\treturn errors.New(\\\"missing mandatory argument: --image\\\")\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tif oldBefore != nil {\\n\\t\\t\\t\\t\\treturn oldBefore(ctx)\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\treturn nil\\n\\t\\t\\t}\\n\\t\\t\\t*cmd = uxImage(*cmd)\\n\\t\\tcase categoryLayout:\\n\\t\\t\\toldBefore := cmd.Before\\n\\t\\t\\tcmd.Before = func(ctx *cli.Context) error {\\n\\t\\t\\t\\tif _, ok := ctx.App.Metadata[\\\"--image-path\\\"]; !ok {\\n\\t\\t\\t\\t\\treturn errors.New(\\\"missing mandatory argument: --layout\\\")\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tif oldBefore != nil {\\n\\t\\t\\t\\t\\treturn oldBefore(ctx)\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\treturn nil\\n\\t\\t\\t}\\n\\t\\t\\t*cmd = uxLayout(*cmd)\\n\\t\\t}\\n\\t})\\n\\n\\terr := app.Run(args)\\n\\tif err != nil {\\n\\t\\t// If an error is a permission based error, give a hint to the user\\n\\t\\t// that --rootless might help. We probably should only be doing this if\\n\\t\\t// we're an unprivileged user.\\n\\t\\tif errors.Is(err, os.ErrPermission) {\\n\\t\\t\\tlog.Warn(\\\"umoci encountered a permission error: maybe --rootless will help?\\\")\\n\\t\\t}\\n\\t\\tlog.Debugf(\\\"%+v\\\", err)\\n\\t}\\n\\treturn err\\n}\\n\\nfunc main() {\\n\\tif err := Main(os.Args); err != nil {\\n\\t\\tlog.Fatalf(\\\"%v\\\", err)\\n\\t}\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(args []string) error {\",\"lineNumber\":65,\"offsetAndLengths\":[[0,10]]},{\"preview\":\"func main() {\",\"lineNumber\":196,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/kubernetes-csi/csi-lib-utils\"},\"file\":{\"path\":\"vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"url\":\"/github.com/kubernetes-csi/csi-lib-utils/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"content\":\"// Copyright 2017 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//    http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\npackage gnostic_extension_v1\\n\\nimport (\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"google.golang.org/protobuf/proto\\\"\\n\\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n)\\n\\ntype extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n\\n// Main implements the main program of an extension handler.\\nfunc Main(handler extensionHandler) {\\n\\t// unpack the request\\n\\tdata, err := ioutil.ReadAll(os.Stdin)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\tif len(data) == 0 {\\n\\t\\tlog.Println(\\\"No input data.\\\")\\n\\t\\tos.Exit(1)\\n\\t}\\n\\trequest := \\u0026ExtensionHandlerRequest{}\\n\\terr = proto.Unmarshal(data, request)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Input error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\t// call the handler\\n\\thandled, output, err := handler(request.Wrapper.ExtensionName, request.Wrapper.Yaml)\\n\\t// respond with the output of the handler\\n\\tresponse := \\u0026ExtensionHandlerResponse{\\n\\t\\tHandled: false, // default assumption\\n\\t\\tErrors:  make([]string, 0),\\n\\t}\\n\\tif err != nil {\\n\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t} else if handled {\\n\\t\\tresponse.Handled = true\\n\\t\\tresponse.Value, err = anypb.New(output)\\n\\t\\tif err != nil {\\n\\t\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t\\t}\\n\\t}\\n\\tresponseBytes, _ := proto.Marshal(response)\\n\\tos.Stdout.Write(responseBytes)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(handler extensionHandler) {\",\"lineNumber\":28,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/openimsdk/open-im-server\"},\"file\":{\"path\":\"tools/seq/internal/seq.go\",\"url\":\"/github.com/openimsdk/open-im-server/-/blob/tools/seq/internal/seq.go\",\"content\":\"package internal\\n\\nimport (\\n\\t\\\"bytes\\\"\\n\\t\\\"context\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"os\\\"\\n\\t\\\"os/signal\\\"\\n\\t\\\"path/filepath\\\"\\n\\t\\\"strconv\\\"\\n\\t\\\"strings\\\"\\n\\t\\\"sync\\\"\\n\\t\\\"sync/atomic\\\"\\n\\t\\\"syscall\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/mitchellh/mapstructure\\\"\\n\\t\\\"github.com/openimsdk/open-im-server/v3/pkg/common/config\\\"\\n\\t\\\"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo\\\"\\n\\t\\\"github.com/openimsdk/tools/db/mongoutil\\\"\\n\\t\\\"github.com/openimsdk/tools/db/redisutil\\\"\\n\\t\\\"github.com/openimsdk/tools/utils/runtimeenv\\\"\\n\\t\\\"github.com/redis/go-redis/v9\\\"\\n\\t\\\"github.com/spf13/viper\\\"\\n\\t\\\"go.mongodb.org/mongo-driver/bson\\\"\\n\\t\\\"go.mongodb.org/mongo-driver/mongo\\\"\\n\\t\\\"go.mongodb.org/mongo-driver/mongo/options\\\"\\n)\\n\\nconst StructTagName = \\\"yaml\\\"\\n\\nconst (\\n\\tMaxSeq                 = \\\"MAX_SEQ:\\\"\\n\\tMinSeq                 = \\\"MIN_SEQ:\\\"\\n\\tConversationUserMinSeq = \\\"CON_USER_MIN_SEQ:\\\"\\n\\tHasReadSeq             = \\\"HAS_READ_SEQ:\\\"\\n)\\n\\nconst (\\n\\tbatchSize             = 100\\n\\tdataVersionCollection = \\\"data_version\\\"\\n\\tseqKey                = \\\"seq\\\"\\n\\tseqVersion            = 38\\n)\\n\\nfunc readConfig[T any](dir string, name string) (*T, error) {\\n\\tif runtimeenv.RuntimeEnvironment() == config.KUBERNETES {\\n\\t\\tdir = os.Getenv(config.MountConfigFilePath)\\n\\t}\\n\\tv := viper.New()\\n\\tv.SetEnvPrefix(config.EnvPrefixMap[name])\\n\\tv.AutomaticEnv()\\n\\tv.SetEnvKeyReplacer(strings.NewReplacer(\\\".\\\", \\\"_\\\"))\\n\\tv.SetConfigFile(filepath.Join(dir, name))\\n\\tif err := v.ReadInConfig(); err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\n\\tvar conf T\\n\\tif err := v.Unmarshal(\\u0026conf, func(config *mapstructure.DecoderConfig) {\\n\\t\\tconfig.TagName = StructTagName\\n\\t}); err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\n\\treturn \\u0026conf, nil\\n}\\n\\nfunc Main(conf string, del time.Duration) error {\\n\\tredisConfig, err := readConfig[config.Redis](conf, config.RedisConfigFileName)\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\t\\n\\tmongodbConfig, err := readConfig[config.Mongo](conf, config.MongodbConfigFileName)\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tctx, cancel := context.WithTimeout(context.Background(), time.Second*10)\\n\\tdefer cancel()\\n\\trdb, err := redisutil.NewRedisClient(ctx, redisConfig.Build())\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tmgocli, err := mongoutil.NewMongoDB(ctx, mongodbConfig.Build())\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tversionColl := mgocli.GetDB().Collection(dataVersionCollection)\\n\\tconverted, err := CheckVersion(versionColl, seqKey, seqVersion)\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tif converted {\\n\\t\\tfmt.Println(\\\"[seq] seq data has been converted\\\")\\n\\t\\treturn nil\\n\\t}\\n\\tif _, err := mgo.NewSeqConversationMongo(mgocli.GetDB()); err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tcSeq, err := mgo.NewSeqConversationMongo(mgocli.GetDB())\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tuSeq, err := mgo.NewSeqUserMongo(mgocli.GetDB())\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tuSpitHasReadSeq := func(id string) (conversationID string, userID string, err error) {\\n\\t\\t// HasReadSeq + userID + \\\":\\\" + conversationID\\n\\t\\tarr := strings.Split(id, \\\":\\\")\\n\\t\\tif len(arr) != 2 || arr[0] == \\\"\\\" || arr[1] == \\\"\\\" {\\n\\t\\t\\treturn \\\"\\\", \\\"\\\", fmt.Errorf(\\\"invalid has read seq id %s\\\", id)\\n\\t\\t}\\n\\t\\tuserID = arr[0]\\n\\t\\tconversationID = arr[1]\\n\\t\\treturn\\n\\t}\\n\\tuSpitConversationUserMinSeq := func(id string) (conversationID string, userID string, err error) {\\n\\t\\t// ConversationUserMinSeq + conversationID + \\\"u:\\\" + userID\\n\\t\\tarr := strings.Split(id, \\\"u:\\\")\\n\\t\\tif len(arr) != 2 || arr[0] == \\\"\\\" || arr[1] == \\\"\\\" {\\n\\t\\t\\treturn \\\"\\\", \\\"\\\", fmt.Errorf(\\\"invalid has read seq id %s\\\", id)\\n\\t\\t}\\n\\t\\tconversationID = arr[0]\\n\\t\\tuserID = arr[1]\\n\\t\\treturn\\n\\t}\\n\\n\\tts := []*taskSeq{\\n\\t\\t{\\n\\t\\t\\tPrefix: MaxSeq,\\n\\t\\t\\tGetSeq: cSeq.GetMaxSeq,\\n\\t\\t\\tSetSeq: cSeq.SetMaxSeq,\\n\\t\\t},\\n\\t\\t{\\n\\t\\t\\tPrefix: MinSeq,\\n\\t\\t\\tGetSeq: cSeq.GetMinSeq,\\n\\t\\t\\tSetSeq: cSeq.SetMinSeq,\\n\\t\\t},\\n\\t\\t{\\n\\t\\t\\tPrefix: HasReadSeq,\\n\\t\\t\\tGetSeq: func(ctx context.Context, id string) (int64, error) {\\n\\t\\t\\t\\tconversationID, userID, err := uSpitHasReadSeq(id)\\n\\t\\t\\t\\tif err != nil {\\n\\t\\t\\t\\t\\treturn 0, err\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\treturn uSeq.GetUserReadSeq(ctx, conversationID, userID)\\n\\t\\t\\t},\\n\\t\\t\\tSetSeq: func(ctx context.Context, id string, seq int64) error {\\n\\t\\t\\t\\tconversationID, userID, err := uSpitHasReadSeq(id)\\n\\t\\t\\t\\tif err != nil {\\n\\t\\t\\t\\t\\treturn err\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\treturn uSeq.SetUserReadSeq(ctx, conversationID, userID, seq)\\n\\t\\t\\t},\\n\\t\\t},\\n\\t\\t{\\n\\t\\t\\tPrefix: ConversationUserMinSeq,\\n\\t\\t\\tGetSeq: func(ctx context.Context, id string) (int64, error) {\\n\\t\\t\\t\\tconversationID, userID, err := uSpitConversationUserMinSeq(id)\\n\\t\\t\\t\\tif err != nil {\\n\\t\\t\\t\\t\\treturn 0, err\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\treturn uSeq.GetUserMinSeq(ctx, conversationID, userID)\\n\\t\\t\\t},\\n\\t\\t\\tSetSeq: func(ctx context.Context, id string, seq int64) error {\\n\\t\\t\\t\\tconversationID, userID, err := uSpitConversationUserMinSeq(id)\\n\\t\\t\\t\\tif err != nil {\\n\\t\\t\\t\\t\\treturn err\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\treturn uSeq.SetUserMinSeq(ctx, conversationID, userID, seq)\\n\\t\\t\\t},\\n\\t\\t},\\n\\t}\\n\\n\\tcancel()\\n\\tctx = context.Background()\\n\\n\\tvar wg sync.WaitGroup\\n\\twg.Add(len(ts))\\n\\n\\tfor i := range ts {\\n\\t\\tgo func(task *taskSeq) {\\n\\t\\t\\tdefer wg.Done()\\n\\t\\t\\terr := seqRedisToMongo(ctx, rdb, task.GetSeq, task.SetSeq, task.Prefix, del, \\u0026task.Count)\\n\\t\\t\\ttask.End = time.Now()\\n\\t\\t\\ttask.Error = err\\n\\t\\t}(ts[i])\\n\\t}\\n\\tstart := time.Now()\\n\\tdone := make(chan struct{})\\n\\tgo func() {\\n\\t\\twg.Wait()\\n\\t\\tclose(done)\\n\\t}()\\n\\n\\tsigs := make(chan os.Signal, 1)\\n\\tsignal.Notify(sigs, syscall.SIGTERM)\\n\\n\\tticker := time.NewTicker(time.Second)\\n\\tdefer ticker.Stop()\\n\\tvar buf bytes.Buffer\\n\\n\\tprintTaskInfo := func(now time.Time) {\\n\\t\\tbuf.Reset()\\n\\t\\tbuf.WriteString(now.Format(time.DateTime))\\n\\t\\tbuf.WriteString(\\\" \\\\n\\\")\\n\\t\\tfor i := range ts {\\n\\t\\t\\ttask := ts[i]\\n\\t\\t\\tif task.Error == nil {\\n\\t\\t\\t\\tif task.End.IsZero() {\\n\\t\\t\\t\\t\\tbuf.WriteString(fmt.Sprintf(\\\"[%s] converting %s* count %d\\\", now.Sub(start), task.Prefix, atomic.LoadInt64(\\u0026task.Count)))\\n\\t\\t\\t\\t} else {\\n\\t\\t\\t\\t\\tbuf.WriteString(fmt.Sprintf(\\\"[%s] success %s* count %d\\\", task.End.Sub(start), task.Prefix, atomic.LoadInt64(\\u0026task.Count)))\\n\\t\\t\\t\\t}\\n\\t\\t\\t} else {\\n\\t\\t\\t\\tbuf.WriteString(fmt.Sprintf(\\\"[%s] failed %s* count %d error %s\\\", task.End.Sub(start), task.Prefix, atomic.LoadInt64(\\u0026task.Count), task.Error))\\n\\t\\t\\t}\\n\\t\\t\\tbuf.WriteString(\\\"\\\\n\\\")\\n\\t\\t}\\n\\t\\tfmt.Println(buf.String())\\n\\t}\\n\\n\\tfor {\\n\\t\\tselect {\\n\\t\\tcase \\u003c-ctx.Done():\\n\\t\\t\\treturn ctx.Err()\\n\\t\\tcase s := \\u003c-sigs:\\n\\t\\t\\treturn fmt.Errorf(\\\"exit by signal %s\\\", s)\\n\\t\\tcase \\u003c-done:\\n\\t\\t\\terrs := make([]error, 0, len(ts))\\n\\t\\t\\tfor i := range ts {\\n\\t\\t\\t\\ttask := ts[i]\\n\\t\\t\\t\\tif task.Error != nil {\\n\\t\\t\\t\\t\\terrs = append(errs, fmt.Errorf(\\\"seq %s failed %w\\\", task.Prefix, task.Error))\\n\\t\\t\\t\\t}\\n\\t\\t\\t}\\n\\t\\t\\tif len(errs) \\u003e 0 {\\n\\t\\t\\t\\treturn errors.Join(errs...)\\n\\t\\t\\t}\\n\\t\\t\\tprintTaskInfo(time.Now())\\n\\t\\t\\tif err := SetVersion(versionColl, seqKey, seqVersion); err != nil {\\n\\t\\t\\t\\treturn fmt.Errorf(\\\"set mongodb seq version %w\\\", err)\\n\\t\\t\\t}\\n\\t\\t\\treturn nil\\n\\t\\tcase now := \\u003c-ticker.C:\\n\\t\\t\\tprintTaskInfo(now)\\n\\t\\t}\\n\\t}\\n}\\n\\ntype taskSeq struct {\\n\\tPrefix string\\n\\tCount  int64\\n\\tError  error\\n\\tEnd    time.Time\\n\\tGetSeq func(ctx context.Context, id string) (int64, error)\\n\\tSetSeq func(ctx context.Context, id string, seq int64) error\\n}\\n\\nfunc seqRedisToMongo(ctx context.Context, rdb redis.UniversalClient, getSeq func(ctx context.Context, id string) (int64, error), setSeq func(ctx context.Context, id string, seq int64) error, prefix string, delAfter time.Duration, count *int64) error {\\n\\tvar (\\n\\t\\tcursor uint64\\n\\t\\tkeys   []string\\n\\t\\terr    error\\n\\t)\\n\\tfor {\\n\\t\\tkeys, cursor, err = rdb.Scan(ctx, cursor, prefix+\\\"*\\\", batchSize).Result()\\n\\t\\tif err != nil {\\n\\t\\t\\treturn err\\n\\t\\t}\\n\\t\\tif len(keys) \\u003e 0 {\\n\\t\\t\\tfor _, key := range keys {\\n\\t\\t\\t\\tseqStr, err := rdb.Get(ctx, key).Result()\\n\\t\\t\\t\\tif err != nil {\\n\\t\\t\\t\\t\\treturn fmt.Errorf(\\\"redis get %s failed %w\\\", key, err)\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tseq, err := strconv.Atoi(seqStr)\\n\\t\\t\\t\\tif err != nil {\\n\\t\\t\\t\\t\\treturn fmt.Errorf(\\\"invalid %s seq %s\\\", key, seqStr)\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tif seq \\u003c 0 {\\n\\t\\t\\t\\t\\treturn fmt.Errorf(\\\"invalid %s seq %s\\\", key, seqStr)\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tid := strings.TrimPrefix(key, prefix)\\n\\t\\t\\t\\tredisSeq := int64(seq)\\n\\t\\t\\t\\tmongoSeq, err := getSeq(ctx, id)\\n\\t\\t\\t\\tif err != nil {\\n\\t\\t\\t\\t\\treturn fmt.Errorf(\\\"get mongo seq %s failed %w\\\", key, err)\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tif mongoSeq \\u003c redisSeq {\\n\\t\\t\\t\\t\\tif err := setSeq(ctx, id, redisSeq); err != nil {\\n\\t\\t\\t\\t\\t\\treturn fmt.Errorf(\\\"set mongo seq %s failed %w\\\", key, err)\\n\\t\\t\\t\\t\\t}\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tif delAfter \\u003e 0 {\\n\\t\\t\\t\\t\\tif err := rdb.Expire(ctx, key, delAfter).Err(); err != nil {\\n\\t\\t\\t\\t\\t\\treturn fmt.Errorf(\\\"redis expire key %s failed %w\\\", key, err)\\n\\t\\t\\t\\t\\t}\\n\\t\\t\\t\\t} else {\\n\\t\\t\\t\\t\\tif err := rdb.Del(ctx, key).Err(); err != nil {\\n\\t\\t\\t\\t\\t\\treturn fmt.Errorf(\\\"redis del key %s failed %w\\\", key, err)\\n\\t\\t\\t\\t\\t}\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tatomic.AddInt64(count, 1)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t\\tif cursor == 0 {\\n\\t\\t\\treturn nil\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc CheckVersion(coll *mongo.Collection, key string, currentVersion int) (converted bool, err error) {\\n\\ttype VersionTable struct {\\n\\t\\tKey   string `bson:\\\"key\\\"`\\n\\t\\tValue string `bson:\\\"value\\\"`\\n\\t}\\n\\tctx, cancel := context.WithTimeout(context.Background(), time.Second*5)\\n\\tdefer cancel()\\n\\tres, err := mongoutil.FindOne[VersionTable](ctx, coll, bson.M{\\\"key\\\": key})\\n\\tif err == nil {\\n\\t\\tver, err := strconv.Atoi(res.Value)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn false, fmt.Errorf(\\\"version %s parse error %w\\\", res.Value, err)\\n\\t\\t}\\n\\t\\tif ver \\u003e= currentVersion {\\n\\t\\t\\treturn true, nil\\n\\t\\t}\\n\\t\\treturn false, nil\\n\\t} else if errors.Is(err, mongo.ErrNoDocuments) {\\n\\t\\treturn false, nil\\n\\t} else {\\n\\t\\treturn false, err\\n\\t}\\n}\\n\\nfunc SetVersion(coll *mongo.Collection, key string, version int) error {\\n\\tctx, cancel := context.WithTimeout(context.Background(), time.Second*5)\\n\\tdefer cancel()\\n\\toption := options.Update().SetUpsert(true)\\n\\tfilter := bson.M{\\\"key\\\": key}\\n\\tupdate := bson.M{\\\"$set\\\": bson.M{\\\"key\\\": key, \\\"value\\\": strconv.Itoa(version)}}\\n\\treturn mongoutil.UpdateOne(ctx, coll, filter, update, false, option)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(conf string, del time.Duration) error {\",\"lineNumber\":69,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/openimsdk/open-im-server\"},\"file\":{\"path\":\"tools/s3/internal/conversion.go\",\"url\":\"/github.com/openimsdk/open-im-server/-/blob/tools/s3/internal/conversion.go\",\"content\":\"package internal\\n\\nimport (\\n\\t\\\"context\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"log\\\"\\n\\t\\\"net/http\\\"\\n\\t\\\"path/filepath\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/mitchellh/mapstructure\\\"\\n\\t\\\"github.com/openimsdk/open-im-server/v3/pkg/common/config\\\"\\n\\t\\\"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis\\\"\\n\\t\\\"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database\\\"\\n\\t\\\"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo\\\"\\n\\t\\\"github.com/openimsdk/tools/db/mongoutil\\\"\\n\\t\\\"github.com/openimsdk/tools/db/redisutil\\\"\\n\\t\\\"github.com/openimsdk/tools/s3\\\"\\n\\t\\\"github.com/openimsdk/tools/s3/aws\\\"\\n\\t\\\"github.com/openimsdk/tools/s3/cos\\\"\\n\\t\\\"github.com/openimsdk/tools/s3/kodo\\\"\\n\\t\\\"github.com/openimsdk/tools/s3/minio\\\"\\n\\t\\\"github.com/openimsdk/tools/s3/oss\\\"\\n\\t\\\"github.com/spf13/viper\\\"\\n\\t\\\"go.mongodb.org/mongo-driver/mongo\\\"\\n)\\n\\nconst defaultTimeout = time.Second * 10\\n\\nfunc readConf(path string, val any) error {\\n\\tv := viper.New()\\n\\tv.SetConfigFile(path)\\n\\tif err := v.ReadInConfig(); err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tfn := func(config *mapstructure.DecoderConfig) {\\n\\t\\tconfig.TagName = \\\"mapstructure\\\"\\n\\t}\\n\\treturn v.Unmarshal(val, fn)\\n}\\n\\nfunc getS3(path string, name string, thirdConf *config.Third) (s3.Interface, error) {\\n\\tswitch name {\\n\\tcase \\\"minio\\\":\\n\\t\\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\\n\\t\\tdefer cancel()\\n\\t\\tvar minioConf config.Minio\\n\\t\\tif err := readConf(filepath.Join(path, minioConf.GetConfigFileName()), \\u0026minioConf); err != nil {\\n\\t\\t\\treturn nil, err\\n\\t\\t}\\n\\t\\tvar redisConf config.Redis\\n\\t\\tif err := readConf(filepath.Join(path, redisConf.GetConfigFileName()), \\u0026redisConf); err != nil {\\n\\t\\t\\treturn nil, err\\n\\t\\t}\\n\\t\\trdb, err := redisutil.NewRedisClient(ctx, redisConf.Build())\\n\\t\\tif err != nil {\\n\\t\\t\\treturn nil, err\\n\\t\\t}\\n\\t\\treturn minio.NewMinio(ctx, redis.NewMinioCache(rdb), *minioConf.Build())\\n\\tcase \\\"cos\\\":\\n\\t\\treturn cos.NewCos(*thirdConf.Object.Cos.Build())\\n\\tcase \\\"oss\\\":\\n\\t\\treturn oss.NewOSS(*thirdConf.Object.Oss.Build())\\n\\tcase \\\"kodo\\\":\\n\\t\\treturn kodo.NewKodo(*thirdConf.Object.Kodo.Build())\\n\\tcase \\\"aws\\\":\\n\\t\\treturn aws.NewAws(*thirdConf.Object.Aws.Build())\\n\\tdefault:\\n\\t\\treturn nil, fmt.Errorf(\\\"invalid object enable: %s\\\", name)\\n\\t}\\n}\\n\\nfunc getMongo(path string) (database.ObjectInfo, error) {\\n\\tvar mongoConf config.Mongo\\n\\tif err := readConf(filepath.Join(path, mongoConf.GetConfigFileName()), \\u0026mongoConf); err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\\n\\tdefer cancel()\\n\\tmgocli, err := mongoutil.NewMongoDB(ctx, mongoConf.Build())\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\treturn mgo.NewS3Mongo(mgocli.GetDB())\\n}\\n\\nfunc Main(path string, engine string) error {\\n\\tvar thirdConf config.Third\\n\\tif err := readConf(filepath.Join(path, thirdConf.GetConfigFileName()), \\u0026thirdConf); err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tif thirdConf.Object.Enable == engine {\\n\\t\\treturn errors.New(\\\"same s3 storage\\\")\\n\\t}\\n\\ts3db, err := getMongo(path)\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\toldS3, err := getS3(path, engine, \\u0026thirdConf)\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tnewS3, err := getS3(path, thirdConf.Object.Enable, \\u0026thirdConf)\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tcount, err := getEngineCount(s3db, oldS3.Engine())\\n\\tif err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tlog.Printf(\\\"engine %s count: %d\\\", oldS3.Engine(), count)\\n\\tvar skip int\\n\\tfor i := 1; i \\u003c= count+1; i++ {\\n\\t\\tlog.Printf(\\\"start %d/%d\\\", i, count)\\n\\t\\tstart := time.Now()\\n\\t\\tres, err := doObject(s3db, newS3, oldS3, skip)\\n\\t\\tif err != nil {\\n\\t\\t\\tlog.Printf(\\\"end [%s] %d/%d error %s\\\", time.Since(start), i, count, err)\\n\\t\\t\\treturn err\\n\\t\\t}\\n\\t\\tlog.Printf(\\\"end [%s] %d/%d result %+v\\\", time.Since(start), i, count, *res)\\n\\t\\tif res.Skip {\\n\\t\\t\\tskip++\\n\\t\\t}\\n\\t\\tif res.End {\\n\\t\\t\\tbreak\\n\\t\\t}\\n\\t}\\n\\treturn nil\\n}\\n\\nfunc getEngineCount(db database.ObjectInfo, name string) (int, error) {\\n\\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\\n\\tdefer cancel()\\n\\tcount, err := db.GetEngineCount(ctx, name)\\n\\tif err != nil {\\n\\t\\treturn 0, err\\n\\t}\\n\\treturn int(count), nil\\n}\\n\\nfunc doObject(db database.ObjectInfo, newS3, oldS3 s3.Interface, skip int) (*Result, error) {\\n\\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\\n\\tdefer cancel()\\n\\tinfos, err := db.GetEngineInfo(ctx, oldS3.Engine(), 1, skip)\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\tif len(infos) == 0 {\\n\\t\\treturn \\u0026Result{End: true}, nil\\n\\t}\\n\\tobj := infos[0]\\n\\tif _, err := db.Take(ctx, newS3.Engine(), obj.Name); err == nil {\\n\\t\\treturn \\u0026Result{Skip: true}, nil\\n\\t} else if !errors.Is(err, mongo.ErrNoDocuments) {\\n\\t\\treturn nil, err\\n\\t}\\n\\tdownloadURL, err := oldS3.AccessURL(ctx, obj.Key, time.Hour, \\u0026s3.AccessURLOption{})\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\tputURL, err := newS3.PresignedPutObject(ctx, obj.Key, time.Hour, \\u0026s3.PutOption{ContentType: obj.ContentType})\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\tdownloadResp, err := http.Get(downloadURL)\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\tdefer downloadResp.Body.Close()\\n\\tswitch downloadResp.StatusCode {\\n\\tcase http.StatusNotFound:\\n\\t\\treturn \\u0026Result{Skip: true}, nil\\n\\tcase http.StatusOK:\\n\\tdefault:\\n\\t\\treturn nil, fmt.Errorf(\\\"download object failed %s\\\", downloadResp.Status)\\n\\t}\\n\\tlog.Printf(\\\"file size %d\\\", obj.Size)\\n\\trequest, err := http.NewRequest(http.MethodPut, putURL.URL, downloadResp.Body)\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\tputResp, err := http.DefaultClient.Do(request)\\n\\tif err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\tdefer putResp.Body.Close()\\n\\tif putResp.StatusCode != http.StatusOK {\\n\\t\\treturn nil, fmt.Errorf(\\\"put object failed %s\\\", putResp.Status)\\n\\t}\\n\\tctx, cancel = context.WithTimeout(context.Background(), defaultTimeout)\\n\\tdefer cancel()\\n\\tif err := db.UpdateEngine(ctx, obj.Engine, obj.Name, newS3.Engine()); err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\treturn \\u0026Result{}, nil\\n}\\n\\ntype Result struct {\\n\\tSkip bool\\n\\tEnd  bool\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(path string, engine string) error {\",\"lineNumber\":87,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/twpayne/chezmoi\"},\"file\":{\"path\":\"internal/cmd/cmd.go\",\"url\":\"/github.com/twpayne/chezmoi/-/blob/internal/cmd/cmd.go\",\"content\":\"// Package cmd contains chezmoi's commands.\\npackage cmd\\n\\nimport (\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"log/slog\\\"\\n\\t\\\"os\\\"\\n\\t\\\"os/exec\\\"\\n\\t\\\"regexp\\\"\\n\\t\\\"runtime/debug\\\"\\n\\t\\\"strconv\\\"\\n\\t\\\"strings\\\"\\n\\n\\t\\\"github.com/spf13/cobra\\\"\\n\\t\\\"github.com/spf13/pflag\\\"\\n\\tbbolterrors \\\"go.etcd.io/bbolt/errors\\\"\\n\\n\\t\\\"chezmoi.io/chezmoi/internal/chezmoi\\\"\\n\\t\\\"chezmoi.io/chezmoi/internal/chezmoierrors\\\"\\n\\t\\\"chezmoi.io/chezmoi/internal/chezmoiset\\\"\\n)\\n\\nconst readSourceStateHookName = \\\"read-source-state\\\"\\n\\nvar (\\n\\tnoArgs = []string(nil)\\n\\n\\tdeDuplicateErrorRx = regexp.MustCompile(`:\\\\s+`)\\n\\n\\tchezmoiDev = make(map[string]string)\\n)\\n\\n// A VersionInfo contains a version.\\ntype VersionInfo struct {\\n\\tVersion string\\n\\tCommit  string\\n\\tDate    string\\n\\tBuiltBy string\\n}\\n\\nfunc (v VersionInfo) LogValue() slog.Value {\\n\\treturn slog.GroupValue(\\n\\t\\tslog.String(\\\"version\\\", v.Version),\\n\\t\\tslog.String(\\\"commit\\\", v.Commit),\\n\\t\\tslog.String(\\\"date\\\", v.Date),\\n\\t\\tslog.String(\\\"builtBy\\\", v.BuiltBy),\\n\\t)\\n}\\n\\n// Main runs chezmoi and returns an exit code.\\nfunc Main(versionInfo VersionInfo, args []string) int {\\n\\tfor pair := range strings.SplitSeq(os.Getenv(\\\"CHEZMOIDEV\\\"), \\\",\\\") {\\n\\t\\tkey, value, _ := strings.Cut(pair, \\\"=\\\")\\n\\t\\tchezmoiDev[key] = value\\n\\t}\\n\\n\\tif err := runMain(versionInfo, args); err != nil {\\n\\t\\tif errExitCode := chezmoi.ExitCodeError(0); errors.As(err, \\u0026errExitCode) {\\n\\t\\t\\treturn int(errExitCode)\\n\\t\\t}\\n\\t\\tfmt.Fprintf(os.Stderr, \\\"chezmoi: %s\\\\n\\\", deDuplicateError(err))\\n\\t\\treturn 1\\n\\t}\\n\\treturn 0\\n}\\n\\n// deDuplicateError returns err's human-readable string with duplicate\\n// components removed.\\nfunc deDuplicateError(err error) string {\\n\\tcomponents := deDuplicateErrorRx.Split(err.Error(), -1)\\n\\tseenComponents := chezmoiset.NewWithCapacity[string](len(components))\\n\\tuniqueComponents := make([]string, 0, len(components))\\n\\tfor _, component := range components {\\n\\t\\tif seenComponents.Contains(component) {\\n\\t\\t\\tcontinue\\n\\t\\t}\\n\\t\\tuniqueComponents = append(uniqueComponents, component)\\n\\t\\tseenComponents.Add(component)\\n\\t}\\n\\treturn strings.Join(uniqueComponents, \\\": \\\")\\n}\\n\\n// example returns command's example.\\nfunc example(command string) string {\\n\\thelp, ok := helps[command]\\n\\tif !ok {\\n\\t\\treturn \\\"\\\"\\n\\t}\\n\\treturn help.example\\n}\\n\\n// markFlagsRequired marks all of flags as required for cmd.\\nfunc markFlagsRequired(cmd *cobra.Command, flags ...string) {\\n\\tfor _, flag := range flags {\\n\\t\\tmust(cmd.MarkFlagRequired(flag))\\n\\t}\\n}\\n\\n// must panics if err is not nil.\\nfunc must(err error) {\\n\\tif err != nil {\\n\\t\\tpanic(err)\\n\\t}\\n}\\n\\n// mustValue panics if err is not nil, otherwise it returns value.\\nfunc mustValue[T any](value T, err error) T {\\n\\tif err != nil {\\n\\t\\tpanic(err)\\n\\t}\\n\\treturn value\\n}\\n\\n// mustValues panics if err is not nil, otherwise it returns value1 and value2.\\nfunc mustValues[T1, T2 any](value1 T1, value2 T2, err error) (T1, T2) {\\n\\tif err != nil {\\n\\t\\tpanic(err)\\n\\t}\\n\\treturn value1, value2\\n}\\n\\n// mustLongHelp returns the long help for command or panics if no long help\\n// exists, unless ignorehelp=1 is set in the CHEZMOIDEV environment variable.\\nfunc mustLongHelp(command string) string {\\n\\thelp, ok := helps[command]\\n\\tif chezmoiDev[\\\"ignorehelp\\\"] != \\\"1\\\" \\u0026\\u0026 (!ok || strings.TrimSpace(help.longHelp) == \\\"\\\") {\\n\\t\\tpanic(command + \\\": missing long help\\\")\\n\\t}\\n\\treturn \\\"Description\\\\n\\\" + help.longHelp\\n}\\n\\n// ensureHasGroupID ensures that cmd has a GroupID set, unless it has the\\n// explicit \\\"hidden\\\" annotation.\\nfunc ensureHasGroupID(cmd *cobra.Command) {\\n\\tif cmd.GroupID == \\\"\\\" \\u0026\\u0026 !cmd.Hidden {\\n\\t\\tpanic(cmd.Name() + \\\": missing group ID\\\")\\n\\t}\\n}\\n\\n// ensureAllFlagsDocumented ensures that all flags are documented, unless\\n// ignoreflags=1 is set in the CHEZMOIDEV environment variable.\\nfunc ensureAllFlagsDocumented(cmd *cobra.Command, persistentFlags *pflag.FlagSet) {\\n\\tif chezmoiDev[\\\"ignoreflags\\\"] == \\\"1\\\" {\\n\\t\\treturn\\n\\t}\\n\\tcmdName := cmd.Name()\\n\\thelp, ok := helps[cmdName]\\n\\tif !ok \\u0026\\u0026 !cmd.Flags().HasFlags() {\\n\\t\\treturn\\n\\t}\\n\\tif !ok {\\n\\t\\tpanic(cmdName + \\\": missing flags\\\")\\n\\t}\\n\\t// Check if all flags are documented.\\n\\tcmd.Flags().VisitAll(func(flag *pflag.Flag) {\\n\\t\\tif _, ok := help.longFlags[flag.Name]; !ok {\\n\\t\\t\\tpanic(fmt.Sprintf(\\\"%s: undocumented long flag --%s\\\", cmdName, flag.Name))\\n\\t\\t}\\n\\t\\tif flag.Shorthand != \\\"\\\" {\\n\\t\\t\\tif _, ok := help.shortFlags[flag.Shorthand]; !ok {\\n\\t\\t\\t\\tpanic(fmt.Sprintf(\\\"%s: undocumented short flag -%s\\\", cmdName, flag.Shorthand))\\n\\t\\t\\t}\\n\\t\\t}\\n\\t})\\n\\t// Check if all documented flags exist.\\n\\tfor flag := range help.longFlags {\\n\\t\\tif cmd.Flags().Lookup(flag) == nil \\u0026\\u0026 persistentFlags.Lookup(flag) == nil {\\n\\t\\t\\tpanic(fmt.Sprintf(\\\"%s: flag --%s documented but not implemented\\\", cmdName, flag))\\n\\t\\t}\\n\\t}\\n\\tfor flag := range help.shortFlags {\\n\\t\\tif cmd.Flags().ShorthandLookup(flag) == nil \\u0026\\u0026 persistentFlags.ShorthandLookup(flag) == nil {\\n\\t\\t\\tpanic(fmt.Sprintf(\\\"%s: flag -%s documented but not implemented\\\", cmdName, flag))\\n\\t\\t}\\n\\t}\\n}\\n\\n// runMain runs chezmoi's main function.\\nfunc runMain(versionInfo VersionInfo, args []string) (err error) {\\n\\tif versionInfo.Commit == \\\"\\\" || versionInfo.Date == \\\"\\\" {\\n\\t\\tif buildInfo, ok := debug.ReadBuildInfo(); ok {\\n\\t\\t\\tvar vcs, vcsRevision, vcsTime, vcsModified string\\n\\t\\t\\tfor _, setting := range buildInfo.Settings {\\n\\t\\t\\t\\tswitch setting.Key {\\n\\t\\t\\t\\tcase \\\"vcs\\\":\\n\\t\\t\\t\\t\\tvcs = setting.Value\\n\\t\\t\\t\\tcase \\\"vcs.revision\\\":\\n\\t\\t\\t\\t\\tvcsRevision = setting.Value\\n\\t\\t\\t\\tcase \\\"vcs.time\\\":\\n\\t\\t\\t\\t\\tvcsTime = setting.Value\\n\\t\\t\\t\\tcase \\\"vcs.modified\\\":\\n\\t\\t\\t\\t\\tvcsModified = setting.Value\\n\\t\\t\\t\\t}\\n\\t\\t\\t}\\n\\t\\t\\tif versionInfo.Commit == \\\"\\\" \\u0026\\u0026 vcs == \\\"git\\\" {\\n\\t\\t\\t\\tversionInfo.Commit = vcsRevision\\n\\t\\t\\t\\tif modified, err := strconv.ParseBool(vcsModified); err == nil \\u0026\\u0026 modified {\\n\\t\\t\\t\\t\\tversionInfo.Commit += \\\"-dirty\\\"\\n\\t\\t\\t\\t}\\n\\t\\t\\t}\\n\\t\\t\\tif versionInfo.Date == \\\"\\\" {\\n\\t\\t\\t\\tversionInfo.Date = vcsTime\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\tvar config *Config\\n\\tif config, err = newConfig(\\n\\t\\twithVersionInfo(versionInfo),\\n\\t); err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tdefer chezmoierrors.CombineFunc(\\u0026err, config.Close)\\n\\n\\tswitch err = config.execute(args); {\\n\\tcase errors.Is(err, bbolterrors.ErrTimeout):\\n\\t\\t// Translate bbolt timeout errors into a friendlier message. As the\\n\\t\\t// persistent state is opened lazily, this error could occur at any\\n\\t\\t// time, so it's easiest to intercept it here.\\n\\t\\treturn errors.New(\\\"timeout obtaining persistent state lock, is another instance of chezmoi running?\\\")\\n\\tcase err != nil \\u0026\\u0026 strings.Contains(err.Error(), \\\"unknown command\\\") \\u0026\\u0026 len(args) \\u003e 0:\\n\\t\\t// If the command is unknown then look for a plugin.\\n\\t\\tif name, lookPathErr := exec.LookPath(\\\"chezmoi-\\\" + args[0]); lookPathErr == nil {\\n\\t\\t\\t// The following is a bit of a hack, as cobra does not have a way to\\n\\t\\t\\t// call a function if a command is not found. We need to run the\\n\\t\\t\\t// pre- and post- run commands to set up the environment, so we\\n\\t\\t\\t// create a fake cobra.Command that corresponds to the name of the\\n\\t\\t\\t// plugin.\\n\\t\\t\\tcmd := \\u0026cobra.Command{\\n\\t\\t\\t\\tUse: args[0],\\n\\t\\t\\t\\tAnnotations: newAnnotations(\\n\\t\\t\\t\\t\\tdoesNotRequireValidConfig,\\n\\t\\t\\t\\t\\tpersistentStateModeEmpty,\\n\\t\\t\\t\\t\\trunsCommands,\\n\\t\\t\\t\\t),\\n\\t\\t\\t}\\n\\t\\t\\tif err := config.persistentPreRunRootE(cmd, args[1:]); err != nil {\\n\\t\\t\\t\\treturn err\\n\\t\\t\\t}\\n\\t\\t\\tpluginCmd := exec.Command(name, args[1:]...)\\n\\t\\t\\tpluginCmd.Stdin = os.Stdin\\n\\t\\t\\tpluginCmd.Stdout = os.Stdout\\n\\t\\t\\tpluginCmd.Stderr = os.Stderr\\n\\t\\t\\terr = config.run(\\\"\\\", name, args[1:])\\n\\t\\t\\tif persistentPostRunRootEErr := config.persistentPostRunRootE(cmd, args[1:]); persistentPostRunRootEErr != nil {\\n\\t\\t\\t\\terr = chezmoierrors.Combine(err, persistentPostRunRootEErr)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\treturn err\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(versionInfo VersionInfo, args []string) int {\",\"lineNumber\":51,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/open-cluster-management-io/clusteradm\"},\"file\":{\"path\":\"vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"url\":\"/github.com/open-cluster-management-io/clusteradm/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"content\":\"// Copyright 2017 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//    http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\npackage gnostic_extension_v1\\n\\nimport (\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"google.golang.org/protobuf/proto\\\"\\n\\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n)\\n\\ntype extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n\\n// Main implements the main program of an extension handler.\\nfunc Main(handler extensionHandler) {\\n\\t// unpack the request\\n\\tdata, err := ioutil.ReadAll(os.Stdin)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\tif len(data) == 0 {\\n\\t\\tlog.Println(\\\"No input data.\\\")\\n\\t\\tos.Exit(1)\\n\\t}\\n\\trequest := \\u0026ExtensionHandlerRequest{}\\n\\terr = proto.Unmarshal(data, request)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Input error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\t// call the handler\\n\\thandled, output, err := handler(request.Wrapper.ExtensionName, request.Wrapper.Yaml)\\n\\t// respond with the output of the handler\\n\\tresponse := \\u0026ExtensionHandlerResponse{\\n\\t\\tHandled: false, // default assumption\\n\\t\\tErrors:  make([]string, 0),\\n\\t}\\n\\tif err != nil {\\n\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t} else if handled {\\n\\t\\tresponse.Handled = true\\n\\t\\tresponse.Value, err = anypb.New(output)\\n\\t\\tif err != nil {\\n\\t\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t\\t}\\n\\t}\\n\\tresponseBytes, _ := proto.Marshal(response)\\n\\tos.Stdout.Write(responseBytes)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(handler extensionHandler) {\",\"lineNumber\":28,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/goplus/spx\"},\"file\":{\"path\":\"internal/engine/engine.go\",\"url\":\"/github.com/goplus/spx/-/blob/internal/engine/engine.go\",\"content\":\"package engine\\n\\nimport (\\n\\t\\\"fmt\\\"\\n\\t\\\"sync\\\"\\n\\n\\tstime \\\"time\\\"\\n\\n\\t\\\"github.com/goplus/spx/v2/internal/engine/profiler\\\"\\n\\t\\\"github.com/goplus/spx/v2/internal/enginewrap\\\"\\n\\t\\\"github.com/goplus/spx/v2/internal/time\\\"\\n\\t\\\"github.com/goplus/spx/v2/pkg/gdspx/pkg/engine\\\"\\n\\tgdx \\\"github.com/goplus/spx/v2/pkg/gdspx/pkg/engine\\\"\\n\\tgde \\\"github.com/goplus/spx/v2/pkg/gdspx/pkg/gdspx\\\"\\n)\\n\\n// copy these variable to any namespace you want\\nvar (\\n\\taudioMgr    enginewrap.AudioMgrImpl\\n\\tcameraMgr   enginewrap.CameraMgrImpl\\n\\tinputMgr    enginewrap.InputMgrImpl\\n\\tphysicMgr   enginewrap.PhysicMgrImpl\\n\\tplatformMgr enginewrap.PlatformMgrImpl\\n\\tresMgr      enginewrap.ResMgrImpl\\n\\textMgr      enginewrap.ExtMgrImpl\\n\\tsceneMgr    enginewrap.SceneMgrImpl\\n\\tspriteMgr   enginewrap.SpriteMgrImpl\\n\\tuiMgr       enginewrap.UiMgrImpl\\n)\\n\\ntype Object = gdx.Object\\ntype Array = gdx.Array\\n\\ntype layerSortMode int\\n\\nconst (\\n\\tlayerSortModeNone layerSortMode = iota\\n\\tlayerSortModeVertical\\n)\\n\\ntype LayerSortInfo struct {\\n\\tX      float64\\n\\tY      float64\\n\\tSprite *Sprite\\n}\\n\\nvar curLayerSortMode layerSortMode\\n\\n// SetLayerSortMode configures automatic layer sorting for sprites.\\n// Supported modes:\\n//   - \\\"\\\" or \\\"none\\\": Disables automatic sorting (default)\\n//   - \\\"vertical\\\": Sorts by Y-coordinate (descending), then X-coordinate (descending)\\n//\\n// When enabled, manual layer control methods are disabled to prevent conflicts.\\nfunc SetLayerSortMode(s string) error {\\n\\tswitch s {\\n\\tcase \\\"\\\", \\\"none\\\":\\n\\t\\tcurLayerSortMode = layerSortModeNone\\n\\tcase \\\"vertical\\\":\\n\\t\\tcurLayerSortMode = layerSortModeVertical\\n\\tdefault:\\n\\t\\treturn fmt.Errorf(\\\"unknown layer sort mode: %s\\\", s)\\n\\t}\\n\\n\\textMgr.SetLayerSorterMode(int64(curLayerSortMode))\\n\\treturn nil\\n}\\n\\nfunc HasLayerSortMethod() bool {\\n\\treturn curLayerSortMode != layerSortModeNone\\n}\\n\\nconst Float2IntFactor = gdx.Float2IntFactor\\n\\nfunc ConvertToFloat64(val int64) float64 {\\n\\treturn float64(val) / engine.Float2IntFactor\\n}\\nfunc ConvertToInt64(val float64) int64 {\\n\\treturn int64(val * engine.Float2IntFactor)\\n}\\n\\ntype TriggerEvent struct {\\n\\tSrc *Sprite\\n\\tDst *Sprite\\n}\\ntype KeyEvent struct {\\n\\tId        int64\\n\\tIsPressed bool\\n}\\n\\nvar (\\n\\tgame              IGame\\n\\ttriggerEventsTemp []TriggerEvent\\n\\ttriggerEvents     []TriggerEvent\\n\\ttriggerMutex      sync.Mutex\\n\\n\\tkeyEventsTemp []KeyEvent\\n\\tkeyEvents     []KeyEvent\\n\\tkeyMutex      sync.Mutex\\n\\n\\t// time\\n\\tstartTimestamp     stime.Time\\n\\tlastTimestamp      stime.Time\\n\\ttimeSinceLevelLoad float64\\n\\n\\tlogicMutex sync.Mutex\\n\\t// statistic info\\n\\tfps float64\\n)\\n\\nfunc Lock() {\\n\\tlogicMutex.Lock()\\n}\\n\\nfunc Unlock() {\\n\\tlogicMutex.Unlock()\\n}\\n\\ntype IGame interface {\\n\\tOnEngineStart()\\n\\tOnEngineUpdate(delta float64)\\n\\tOnEngineRender(delta float64)\\n\\tOnEngineDestroy()\\n\\tOnEnginePause(isPaused bool)\\n}\\n\\nfunc Main(g IGame) {\\n\\tenginewrap.Init(WaitMainThread)\\n\\tgame = g\\n\\tgde.LinkEngine(gdx.EngineCallbackInfo{\\n\\t\\tOnEngineStart:   onStart,\\n\\t\\tOnEngineUpdate:  onUpdate,\\n\\t\\tOnEngineDestroy: onDestroy,\\n\\t\\tOnEnginePause:   onPaused,\\n\\t\\tOnKeyPressed:    onKeyPressed,\\n\\t\\tOnKeyReleased:   onKeyReleased,\\n\\t})\\n}\\n\\nfunc RegisterFuncs() {\\n\\n}\\nfunc OnGameStarted() {\\n\\tgco.OnInited()\\n}\\n\\n// callbacks\\nfunc onStart() {\\n\\tdefer CheckPanic()\\n\\ttriggerEventsTemp = make([]TriggerEvent, 0)\\n\\ttriggerEvents = make([]TriggerEvent, 0)\\n\\tkeyEventsTemp = make([]KeyEvent, 0)\\n\\tkeyEvents = make([]KeyEvent, 0)\\n\\n\\ttime.Start(func(scale float64) {\\n\\t\\tplatformMgr.SetTimeScale(scale)\\n\\t})\\n\\n\\tstartTimestamp = stime.Now()\\n\\tlastTimestamp = stime.Now()\\n\\tgame.OnEngineStart()\\n}\\n\\nfunc onUpdate(delta float64) {\\n\\tdefer CheckPanic()\\n\\tprofiler.BeginSample()\\n\\tupdateTime(float64(delta))\\n\\tcacheTriggerEvents()\\n\\tcacheKeyEvents()\\n\\tprofiler.MeasureFunctionTime(\\\"GameUpdate\\\", func() {\\n\\t\\tgame.OnEngineUpdate(delta)\\n\\t})\\n\\tprofiler.MeasureFunctionTime(\\\"CoroUpdateJobs\\\", func() {\\n\\t\\tgco.Update()\\n\\t})\\n\\tprofiler.MeasureFunctionTime(\\\"GameRender\\\", func() {\\n\\t\\tgame.OnEngineRender(delta)\\n\\t})\\n\\tprofiler.EndSample()\\n}\\n\\nfunc onDestroy() {\\n\\tgame.OnEngineDestroy()\\n}\\n\\nfunc onPaused(isPaused bool) {\\n\\tgame.OnEnginePause(isPaused)\\n}\\n\\nfunc onKeyPressed(id int64) {\\n\\tkeyEventsTemp = append(keyEventsTemp, KeyEvent{Id: id, IsPressed: true})\\n}\\n\\nfunc onKeyReleased(id int64) {\\n\\tkeyEventsTemp = append(keyEventsTemp, KeyEvent{Id: id, IsPressed: false})\\n}\\n\\nfunc updateTime(delta float64) {\\n\\tdeltaTime := delta\\n\\ttimeSinceLevelLoad += deltaTime\\n\\n\\tcurTime := stime.Now()\\n\\tunscaledTimeSinceLevelLoad := curTime.Sub(startTimestamp).Seconds()\\n\\tunscaledDeltaTime := curTime.Sub(lastTimestamp).Seconds()\\n\\tlastTimestamp = curTime\\n\\ttimeScale := SyncGetTimeScale()\\n\\tfps = profiler.Calcfps()\\n\\ttime.Update(float64(timeScale), unscaledTimeSinceLevelLoad, timeSinceLevelLoad, deltaTime, unscaledDeltaTime, fps)\\n}\\n\\nfunc cacheTriggerEvents() {\\n\\ttriggerMutex.Lock()\\n\\ttriggerEvents = append(triggerEvents, triggerEventsTemp...)\\n\\ttriggerMutex.Unlock()\\n\\ttriggerEventsTemp = triggerEventsTemp[:0]\\n}\\nfunc GetTriggerEvents(lst []TriggerEvent) []TriggerEvent {\\n\\ttriggerMutex.Lock()\\n\\tlst = append(lst, triggerEvents...)\\n\\ttriggerEvents = triggerEvents[:0]\\n\\ttriggerMutex.Unlock()\\n\\treturn lst\\n}\\nfunc cacheKeyEvents() {\\n\\tkeyMutex.Lock()\\n\\tkeyEvents = append(keyEvents, keyEventsTemp...)\\n\\tkeyMutex.Unlock()\\n\\tkeyEventsTemp = keyEventsTemp[:0]\\n}\\n\\nfunc GetKeyEvents(lst []KeyEvent) []KeyEvent {\\n\\tkeyMutex.Lock()\\n\\tlst = append(lst, keyEvents...)\\n\\tkeyEvents = keyEvents[:0]\\n\\tkeyMutex.Unlock()\\n\\treturn lst\\n}\\n\\nfunc CheckPanic() {\\n\\tif e := recover(); e != nil {\\n\\t\\tOnPanic(\\\"\\\", \\\"\\\")\\n\\t\\tpanic(e)\\n\\t}\\n}\\n\\nfunc OnPanic(name, stack string) {\\n\\t// on coro panic, exit game\\n\\tmsg := name\\n\\tif stack != \\\"\\\" {\\n\\t\\tmsg += \\\" stack:\\\\n\\\" + stack\\n\\t}\\n\\textMgr.OnRuntimePanic(msg)\\n\\tRequestExit(1)\\n}\\n\\nfunc RequestExit(exitCode int64) {\\n\\textMgr.RequestExit(exitCode)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(g IGame) {\",\"lineNumber\":126,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/open-runtimes/open-runtimes\"},\"file\":{\"path\":\"tests/resources/functions/go/latest/tests.go\",\"url\":\"/github.com/open-runtimes/open-runtimes/-/blob/tests/resources/functions/go/latest/tests.go\",\"content\":\"package handler\\n\\nimport (\\n\\t\\\"crypto/md5\\\"\\n\\t\\\"encoding/hex\\\"\\n\\t\\\"encoding/json\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"os\\\"\\n\\t\\\"strings\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/go-resty/resty/v2\\\"\\n\\t\\\"github.com/open-runtimes/types-for-go/v4/openruntimes\\\"\\n)\\n\\nfunc Main(Context openruntimes.Context) openruntimes.Response {\\n\\taction := Context.Req.Headers[\\\"x-action\\\"]\\n\\n\\tswitch a := action; a {\\n\\tcase \\\"plaintextResponse\\\":\\n\\t\\treturn Context.Res.Text(\\\"Hello World \U0001F44B\\\")\\n\\tcase \\\"jsonResponse\\\":\\n\\t\\treturn Context.Res.Json(map[string]interface{}{\\n\\t\\t\\t\\\"json\\\":    true,\\n\\t\\t\\t\\\"message\\\": \\\"Developers are awesome.\\\",\\n\\t\\t})\\n\\tcase \\\"customCharsetResponse\\\":\\n\\t\\treturn Context.Res.Text(\\\"ÅÆ\\\", Context.Res.WithHeaders(map[string]string{\\n\\t\\t\\t\\\"content-type\\\": \\\"text/plain; charset=iso-8859-1\\\",\\n\\t\\t}))\\n\\tcase \\\"uppercaseCharsetResponse\\\":\\n\\t\\treturn Context.Res.Text(\\\"ÅÆ\\\", Context.Res.WithHeaders(map[string]string{\\n\\t\\t\\t\\\"content-type\\\": \\\"TEXT/PLAIN\\\",\\n\\t\\t}))\\n\\tcase \\\"multipartResponse\\\":\\n\\t\\treturn Context.Res.Text(`--12345\\nContent-Disposition: form-data; name=\\\"partOne\\\"\\n\\nWhy just have one part?\\n--12345\\nContent-Disposition: form-data; name=\\\"partTwo\\\"\\n\\nWhen you can have two!\\n--12345--`, Context.Res.WithHeaders(map[string]string{\\n\\t\\t\\t\\\"content-type\\\": \\\"multipart/form-data; boundary=12345\\\",\\n\\t\\t}))\\n\\tcase \\\"redirectResponse\\\":\\n\\t\\treturn Context.Res.Redirect(\\\"https://github.com/\\\", Context.Res.WithStatusCode(301))\\n\\tcase \\\"emptyResponse\\\":\\n\\t\\treturn Context.Res.Empty()\\n\\tcase \\\"noResponse\\\":\\n\\t\\tContext.Res.Text(\\\"This should be ignored, as it is not returned.\\\")\\n\\n\\t\\t// Simulate test data. Return nessessary in Golang\\n\\t\\tContext.Error(\\\"Return statement missing. return context.getRes().empty() if no response is expected.\\\")\\n\\t\\treturn Context.Res.Text(\\\"\\\", Context.Res.WithStatusCode(500))\\n\\tcase \\\"doubleResponse\\\":\\n\\t\\tContext.Res.Text(\\\"This should be ignored.\\\")\\n\\t\\treturn Context.Res.Text(\\\"This should be returned.\\\")\\n\\tcase \\\"enforcedHeaders\\\":\\n\\t\\treturn Context.Res.Json(map[string]interface{}{\\n\\t\\t\\t\\\"x-custom\\\":               Context.Req.Headers[\\\"x-custom\\\"],\\n\\t\\t\\t\\\"x-custom-uppercase\\\":     Context.Req.Headers[\\\"x-custom-uppercase\\\"],\\n\\t\\t\\t\\\"x-open-runtimes-custom\\\": Context.Req.Headers[\\\"x-open-runtimes-custom\\\"],\\n\\t\\t})\\n\\tcase \\\"headersResponse\\\":\\n\\t\\tsecondHeader, ok := Context.Req.Headers[\\\"x-open-runtimes-custom-in-header\\\"]\\n\\t\\tif !ok {\\n\\t\\t\\tsecondHeader = \\\"missing\\\"\\n\\t\\t}\\n\\n\\t\\tcookie, ok := Context.Req.Headers[\\\"cookie\\\"]\\n\\t\\tif !ok {\\n\\t\\t\\tcookie = \\\"missing\\\"\\n\\t\\t}\\n\\n\\t\\treturn Context.Res.Text(\\\"OK\\\", Context.Res.WithHeaders(map[string]string{\\n\\t\\t\\t\\\"first-header\\\":                      \\\"first-value\\\",\\n\\t\\t\\t\\\"second-header\\\":                     secondHeader,\\n\\t\\t\\t\\\"cookie\\\":                            cookie,\\n\\t\\t\\t\\\"x-open-runtimes-custom-out-header\\\": \\\"third-value\\\",\\n\\t\\t}))\\n\\tcase \\\"statusResponse\\\":\\n\\t\\treturn Context.Res.Text(\\\"FAIL\\\", Context.Res.WithStatusCode(404))\\n\\tcase \\\"requestMethod\\\":\\n\\t\\treturn Context.Res.Text(Context.Req.Method)\\n\\tcase \\\"requestUrl\\\":\\n\\t\\treturn Context.Res.Json(map[string]interface{}{\\n\\t\\t\\t\\\"url\\\":         Context.Req.Url,\\n\\t\\t\\t\\\"port\\\":        Context.Req.Port,\\n\\t\\t\\t\\\"path\\\":        Context.Req.Path,\\n\\t\\t\\t\\\"query\\\":       Context.Req.Query,\\n\\t\\t\\t\\\"queryString\\\": Context.Req.QueryString,\\n\\t\\t\\t\\\"scheme\\\":      Context.Req.Scheme,\\n\\t\\t\\t\\\"host\\\":        Context.Req.Host,\\n\\t\\t})\\n\\tcase \\\"requestHeaders\\\":\\n\\t\\treturn Context.Res.Json(Context.Req.Headers)\\n\\tcase \\\"requestBodyText\\\":\\n\\t\\treturn Context.Res.Text(Context.Req.BodyText())\\n\\tcase \\\"requestBodyJson\\\":\\n\\t\\tvar bodyJson map[string]interface{}\\n\\t\\terr := Context.Req.BodyJson(\\u0026bodyJson)\\n\\n\\t\\tif err != nil {\\n\\t\\t\\tContext.Error(\\\"Cannot parse JSON body\\\")\\n\\t\\t\\treturn Context.Res.Text(\\\"\\\", Context.Res.WithStatusCode(500))\\n\\t\\t}\\n\\n\\t\\treturn Context.Res.Json(bodyJson)\\n\\tcase \\\"requestBodyBinary\\\":\\n\\t\\treturn Context.Res.Binary(Context.Req.BodyBinary())\\n\\tcase \\\"requestBodyTextAuto\\\":\\n\\t\\treturn Context.Res.Text(Context.Req.BodyText())\\n\\tcase \\\"requestBodyJsonAuto\\\":\\n\\t\\treturn Context.Res.Json(Context.Req.Body().(map[string]interface{}))\\n\\tcase \\\"binaryResponse1\\\":\\n\\t\\tbytes := []byte{0, 10, 255}\\n\\t\\treturn Context.Res.Binary(bytes) // []byte\\n\\tcase \\\"binaryResponse2\\\":\\n\\t\\tbytes := []byte{0, 20, 255}\\n\\t\\treturn Context.Res.Binary(bytes) // Just a filler\\n\\tcase \\\"binaryResponse3\\\":\\n\\t\\tbytes := []byte{0, 30, 255}\\n\\t\\treturn Context.Res.Binary(bytes) // Just a filler\\n\\tcase \\\"binaryResponse4\\\":\\n\\t\\tbytes := []byte{0, 40, 255}\\n\\t\\treturn Context.Res.Binary(bytes) // Just a filler\\n\\tcase \\\"binaryResponse5\\\":\\n\\t\\tbytes := []byte{0, 50, 255}\\n\\t\\treturn Context.Res.Binary(bytes) // Just a filler\\n\\tcase \\\"envVars\\\":\\n\\t\\tvar emptyVar *string\\n\\t\\tvarValue := os.Getenv(\\\"NOT_DEFINED_VAR\\\")\\n\\t\\tif varValue != \\\"\\\" {\\n\\t\\t\\temptyVar = \\u0026varValue\\n\\t\\t}\\n\\n\\t\\treturn Context.Res.Json(map[string]interface{}{\\n\\t\\t\\t\\\"var\\\":      os.Getenv(\\\"CUSTOM_ENV_VAR\\\"),\\n\\t\\t\\t\\\"emptyVar\\\": emptyVar,\\n\\t\\t})\\n\\tcase \\\"logs\\\":\\n\\t\\tfmt.Println(\\\"Native log\\\")\\n\\t\\tContext.Log(errors.New(\\\"Debug log\\\"))\\n\\t\\tContext.Error(\\\"Error log\\\")\\n\\n\\t\\tContext.Log(\\\"Log+With+Plus+Symbol\\\")\\n\\n\\t\\tContext.Log(42)\\n\\t\\tContext.Log(4.2)\\n\\t\\tContext.Log(true)\\n\\n\\t\\tContext.Log(map[string]string{\\\"objectKey\\\": \\\"objectValue\\\"})\\n\\t\\tContext.Log([]string{\\\"arrayValue\\\"})\\n\\n\\t\\tContext.Log(strings.Repeat(\\\"A\\\", 9000))\\n\\t\\tContext.Error(strings.Repeat(\\\"B\\\", 9000))\\n\\n\\t\\treturn Context.Res.Text(\\\"\\\")\\n\\tcase \\\"library\\\":\\n\\t\\tclient := resty.New()\\n\\t\\tresp, errResponse := client.R().\\n\\t\\t\\tSetHeader(\\\"Accept\\\", \\\"application/json\\\").\\n\\t\\t\\tGet(\\\"https://dummyjson.com/todos/\\\" + Context.Req.BodyText())\\n\\n\\t\\tif errResponse != nil {\\n\\t\\t\\tContext.Error(errResponse)\\n\\t\\t\\treturn Context.Res.Text(\\\"\\\", Context.Res.WithStatusCode(500))\\n\\t\\t}\\n\\n\\t\\tbody := resp.String()\\n\\n\\t\\ttype TodoObject struct {\\n\\t\\t\\tUserId    int    `json:\\\"userId\\\"`\\n\\t\\t\\tId        int    `json:\\\"id\\\"`\\n\\t\\t\\tTodo      string `json:\\\"todo\\\"`\\n\\t\\t\\tCompleted bool   `json:\\\"completed\\\"`\\n\\t\\t}\\n\\n\\t\\tvar todo TodoObject\\n\\t\\terrJson := json.Unmarshal([]byte(body), \\u0026todo)\\n\\n\\t\\tif errJson != nil {\\n\\t\\t\\tContext.Error(errJson)\\n\\t\\t\\treturn Context.Res.Text(\\\"\\\", Context.Res.WithStatusCode(500))\\n\\t\\t}\\n\\n\\t\\treturn Context.Res.Json(map[string]interface{}{\\n\\t\\t\\t\\\"todo\\\": todo,\\n\\t\\t})\\n\\tcase \\\"timeout\\\":\\n\\t\\tContext.Log(\\\"Timeout start.\\\")\\n\\n\\t\\ttime.Sleep(3 * time.Second)\\n\\n\\t\\tContext.Log(\\\"Timeout end.\\\")\\n\\n\\t\\treturn Context.Res.Text(\\\"Successful response.\\\")\\n\\tcase \\\"deprecatedMethods\\\":\\n\\t\\treturn Context.Res.Send(Context.Req.BodyRaw())\\n\\tcase \\\"deprecatedMethodsUntypedBody\\\":\\n\\t\\treturn Context.Res.Send(\\\"50\\\") // Send only supported String\\n\\tcase \\\"binaryResponseLarge\\\":\\n\\t\\thashBinary := md5.Sum(Context.Req.BodyBinary())\\n\\t\\thashHex := hex.EncodeToString(hashBinary[:])\\n\\t\\treturn Context.Res.Text(hashHex, Context.Res.WithHeaders(map[string]string{\\n\\t\\t\\t\\\"x-method\\\": Context.Req.Method,\\n\\t\\t}))\\n\\tcase \\\"spreadOperatorLogs\\\":\\n\\t\\tengine := \\\"open-runtimes\\\"\\n\\t\\tContext.Log(\\\"engine:\\\", engine)\\n\\t\\tContext.Error(\\\"engine:\\\", engine)\\n\\t\\treturn Context.Res.Text(\\\"OK\\\")\\n\\tcase \\\"errorTest\\\":\\n\\t\\tContext.Log(\\\"Before error...\\\")\\n\\t\\tpanic(\\\"Error!\\\")\\n\\t\\treturn Context.Res.Text(\\\"This should never be reached\\\")\\n\\tdefault:\\n\\t\\tContext.Error(\\\"Unknown action in tests.go\\\")\\n\\t\\treturn Context.Res.Text(\\\"\\\", Context.Res.WithStatusCode(500))\\n\\t}\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(Context openruntimes.Context) openruntimes.Response {\",\"lineNumber\":16,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/google/certificate-transparency-go\"},\"file\":{\"path\":\"internal/witness/cmd/witness/impl/witness.go\",\"url\":\"/github.com/google/certificate-transparency-go/-/blob/internal/witness/cmd/witness/impl/witness.go\",\"content\":\"// Copyright 2021 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//     http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\n// Package impl is the implementation of the witness server.\\npackage impl\\n\\nimport (\\n\\t\\\"context\\\"\\n\\t\\\"crypto/sha256\\\"\\n\\t\\\"database/sql\\\"\\n\\t\\\"encoding/base64\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"net/http\\\"\\n\\n\\tct \\\"github.com/google/certificate-transparency-go\\\"\\n\\tih \\\"github.com/google/certificate-transparency-go/internal/witness/cmd/witness/internal/http\\\"\\n\\t\\\"github.com/google/certificate-transparency-go/internal/witness/cmd/witness/internal/witness\\\"\\n\\t\\\"github.com/gorilla/mux\\\"\\n\\t_ \\\"github.com/mattn/go-sqlite3\\\" // Load drivers for sqlite3\\n\\t\\\"k8s.io/klog/v2\\\"\\n)\\n\\n// LogConfig contains a list of LogInfo (configuration options for a log).\\ntype LogConfig struct {\\n\\tLogs []LogInfo `yaml:\\\"Logs\\\"`\\n}\\n\\n// LogInfo contains the configuration options for a log, which is just its public key.\\ntype LogInfo struct {\\n\\tPubKey string `yaml:\\\"PubKey\\\"`\\n}\\n\\n// ServerOpts provides the options for a server (specified in main.go).\\ntype ServerOpts struct {\\n\\t// Where to listen for requests.\\n\\tListenAddr string\\n\\t// The file for sqlite3 storage.\\n\\tDBFile string\\n\\t// The signing key for the witness.\\n\\tPrivKey string\\n\\t// The log configuration information.\\n\\tConfig LogConfig\\n}\\n\\n// buildLogMap loads the log configuration information into a map.\\nfunc buildLogMap(config LogConfig) (map[string]ct.SignatureVerifier, error) {\\n\\tlogMap := make(map[string]ct.SignatureVerifier)\\n\\tfor _, log := range config.Logs {\\n\\t\\t// Use the PubKey string to first create the verifier.\\n\\t\\tpk, err := ct.PublicKeyFromB64(log.PubKey)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn nil, fmt.Errorf(\\\"failed to create public key: %v\\\", err)\\n\\t\\t}\\n\\t\\tlogV, err := ct.NewSignatureVerifier(pk)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn nil, fmt.Errorf(\\\"failed to create signature verifier: %v\\\", err)\\n\\t\\t}\\n\\t\\t// And then to create the logID.\\n\\t\\tlogID, err := LogIDFromPubKey(log.PubKey)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn nil, fmt.Errorf(\\\"failed to create log id: %v\\\", err)\\n\\t\\t}\\n\\t\\tlogMap[logID] = *logV\\n\\t}\\n\\treturn logMap, nil\\n}\\n\\n// LogIDFromPubKey builds the logID given the base64-encoded public key.\\nfunc LogIDFromPubKey(pk string) (string, error) {\\n\\tder, err := base64.StdEncoding.DecodeString(pk)\\n\\tif err != nil {\\n\\t\\treturn \\\"\\\", fmt.Errorf(\\\"failed to decode public key: %v\\\", err)\\n\\t}\\n\\tsha := sha256.Sum256(der)\\n\\treturn base64.StdEncoding.EncodeToString(sha[:]), nil\\n}\\n\\n// Main sets up and runs the witness given the options.\\nfunc Main(ctx context.Context, opts ServerOpts) error {\\n\\tif len(opts.DBFile) == 0 {\\n\\t\\treturn errors.New(\\\"DBFile is required\\\")\\n\\t}\\n\\t// Start up local database.\\n\\tklog.Infof(\\\"Connecting to local DB at %q\\\", opts.DBFile)\\n\\tdb, err := sql.Open(\\\"sqlite3\\\", opts.DBFile)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to connect to DB: %w\\\", err)\\n\\t}\\n\\n\\t// Avoid multiple writes colliding and resulting in a \\\"database locked\\\" error.\\n\\tdb.SetMaxOpenConns(1)\\n\\n\\t// Load log configuration into the map.\\n\\tlogMap, err := buildLogMap(opts.Config)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to load configurations: %v\\\", err)\\n\\t}\\n\\n\\tw, err := witness.New(witness.Opts{\\n\\t\\tDB:        db,\\n\\t\\tPrivKey:   opts.PrivKey,\\n\\t\\tKnownLogs: logMap,\\n\\t})\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"error creating witness: %v\\\", err)\\n\\t}\\n\\n\\tklog.Infof(\\\"Starting witness server...\\\")\\n\\tsrv := ih.NewServer(w)\\n\\t// These options are required as some logID values contain forward\\n\\t// slashes, and will be PathUnescape-d later.\\n\\tr := mux.NewRouter().UseEncodedPath()\\n\\tsrv.RegisterHandlers(r)\\n\\thServer := \\u0026http.Server{\\n\\t\\tAddr:    opts.ListenAddr,\\n\\t\\tHandler: r,\\n\\t}\\n\\te := make(chan error, 1)\\n\\tgo func() {\\n\\t\\te \\u003c- hServer.ListenAndServe()\\n\\t\\tclose(e)\\n\\t}()\\n\\t\\u003c-ctx.Done()\\n\\tklog.Info(\\\"Server shutting down\\\")\\n\\tif err := hServer.Shutdown(ctx); err != nil {\\n\\t\\tklog.Errorf(\\\"Shutdown(): %v\\\", err)\\n\\t}\\n\\treturn \\u003c-e\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(ctx context.Context, opts ServerOpts) error {\",\"lineNumber\":90,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/kubernetes-sigs/aws-efs-csi-driver\"},\"file\":{\"path\":\"vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"url\":\"/github.com/kubernetes-sigs/aws-efs-csi-driver/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"content\":\"// Copyright 2017 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//    http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\npackage gnostic_extension_v1\\n\\nimport (\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"google.golang.org/protobuf/proto\\\"\\n\\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n)\\n\\ntype extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n\\n// Main implements the main program of an extension handler.\\nfunc Main(handler extensionHandler) {\\n\\t// unpack the request\\n\\tdata, err := ioutil.ReadAll(os.Stdin)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\tif len(data) == 0 {\\n\\t\\tlog.Println(\\\"No input data.\\\")\\n\\t\\tos.Exit(1)\\n\\t}\\n\\trequest := \\u0026ExtensionHandlerRequest{}\\n\\terr = proto.Unmarshal(data, request)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Input error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\t// call the handler\\n\\thandled, output, err := handler(request.Wrapper.ExtensionName, request.Wrapper.Yaml)\\n\\t// respond with the output of the handler\\n\\tresponse := \\u0026ExtensionHandlerResponse{\\n\\t\\tHandled: false, // default assumption\\n\\t\\tErrors:  make([]string, 0),\\n\\t}\\n\\tif err != nil {\\n\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t} else if handled {\\n\\t\\tresponse.Handled = true\\n\\t\\tresponse.Value, err = anypb.New(output)\\n\\t\\tif err != nil {\\n\\t\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t\\t}\\n\\t}\\n\\tresponseBytes, _ := proto.Marshal(response)\\n\\tos.Stdout.Write(responseBytes)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(handler extensionHandler) {\",\"lineNumber\":28,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/knative/pkg\"},\"file\":{\"path\":\"injection/sharedmain/main.go\",\"url\":\"/github.com/knative/pkg/-/blob/injection/sharedmain/main.go\",\"content\":\"/*\\nCopyright 2019 The Knative Authors\\n\\nLicensed under the Apache License, Version 2.0 (the \\\"License\\\");\\nyou may not use this file except in compliance with the License.\\nYou may obtain a copy of the License at\\n\\n    http://www.apache.org/licenses/LICENSE-2.0\\n\\nUnless required by applicable law or agreed to in writing, software\\ndistributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\nSee the License for the specific language governing permissions and\\nlimitations under the License.\\n*/\\n\\npackage sharedmain\\n\\nimport (\\n\\t\\\"context\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"flag\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"log\\\"\\n\\t\\\"net/http\\\"\\n\\t\\\"os\\\"\\n\\t\\\"strconv\\\"\\n\\t\\\"strings\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"go.opentelemetry.io/contrib/instrumentation/runtime\\\"\\n\\t\\\"go.opentelemetry.io/otel\\\"\\n\\t\\\"go.opentelemetry.io/otel/sdk/metric\\\"\\n\\t\\\"go.opentelemetry.io/otel/sdk/trace\\\"\\n\\n\\t\\\"github.com/go-logr/zapr\\\"\\n\\t\\\"go.uber.org/automaxprocs/maxprocs\\\" // automatically set GOMAXPROCS based on cgroups\\n\\t\\\"go.uber.org/zap\\\"\\n\\t\\\"golang.org/x/sync/errgroup\\\"\\n\\n\\tcorev1 \\\"k8s.io/api/core/v1\\\"\\n\\tapierrors \\\"k8s.io/apimachinery/pkg/api/errors\\\"\\n\\tmetav1 \\\"k8s.io/apimachinery/pkg/apis/meta/v1\\\"\\n\\t\\\"k8s.io/apimachinery/pkg/labels\\\"\\n\\t\\\"k8s.io/apimachinery/pkg/util/sets\\\"\\n\\t\\\"k8s.io/apimachinery/pkg/util/wait\\\"\\n\\t\\\"k8s.io/client-go/rest\\\"\\n\\tk8stoolmetrics \\\"k8s.io/client-go/tools/metrics\\\"\\n\\t\\\"k8s.io/client-go/util/workqueue\\\"\\n\\t\\\"k8s.io/klog/v2\\\"\\n\\n\\tkubeclient \\\"knative.dev/pkg/client/injection/kube/client\\\"\\n\\t\\\"knative.dev/pkg/configmap\\\"\\n\\tcminformer \\\"knative.dev/pkg/configmap/informer\\\"\\n\\t\\\"knative.dev/pkg/controller\\\"\\n\\t\\\"knative.dev/pkg/injection\\\"\\n\\t\\\"knative.dev/pkg/leaderelection\\\"\\n\\t\\\"knative.dev/pkg/logging\\\"\\n\\t\\\"knative.dev/pkg/logging/logkey\\\"\\n\\t\\\"knative.dev/pkg/observability\\\"\\n\\to11yconfigmap \\\"knative.dev/pkg/observability/configmap\\\"\\n\\t\\\"knative.dev/pkg/observability/metrics\\\"\\n\\tk8smetrics \\\"knative.dev/pkg/observability/metrics/k8s\\\"\\n\\t\\\"knative.dev/pkg/observability/resource\\\"\\n\\tk8sruntime \\\"knative.dev/pkg/observability/runtime/k8s\\\"\\n\\t\\\"knative.dev/pkg/observability/tracing\\\"\\n\\t\\\"knative.dev/pkg/reconciler\\\"\\n\\t\\\"knative.dev/pkg/signals\\\"\\n\\t\\\"knative.dev/pkg/system\\\"\\n\\t\\\"knative.dev/pkg/version\\\"\\n\\t\\\"knative.dev/pkg/webhook\\\"\\n)\\n\\nfunc init() {\\n\\tmaxprocs.Set()\\n}\\n\\n// GetLoggingConfig gets the logging config from the (in order):\\n// 1. provided context,\\n// 2. reading from the API server,\\n// 3. defaults (if not found).\\n// The context is expected to be initialized with injection.\\nfunc GetLoggingConfig(ctx context.Context) (*logging.Config, error) {\\n\\tif cfg := logging.GetConfig(ctx); cfg != nil {\\n\\t\\treturn cfg, nil\\n\\t}\\n\\n\\tvar loggingConfigMap *corev1.ConfigMap\\n\\t// These timeout and retry interval are set by heuristics.\\n\\t// e.g. istio sidecar needs a few seconds to configure the pod network.\\n\\tvar lastErr error\\n\\tif err := wait.PollUntilContextTimeout(ctx, 1*time.Second, 5*time.Second, true, func(ctx context.Context) (bool, error) {\\n\\t\\tloggingConfigMap, lastErr = kubeclient.Get(ctx).CoreV1().ConfigMaps(system.Namespace()).Get(ctx, logging.ConfigMapName(), metav1.GetOptions{})\\n\\t\\treturn lastErr == nil || apierrors.IsNotFound(lastErr), nil\\n\\t}); err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"timed out waiting for the condition: %w\\\", lastErr)\\n\\t}\\n\\tif loggingConfigMap == nil {\\n\\t\\treturn logging.NewConfigFromMap(nil)\\n\\t}\\n\\treturn logging.NewConfigFromConfigMap(loggingConfigMap)\\n}\\n\\n// GetLeaderElectionConfig gets the leader election config from the (in order):\\n// 1. provided context,\\n// 2. reading from the API server,\\n// 3. defaults (if not found).\\nfunc GetLeaderElectionConfig(ctx context.Context) (*leaderelection.Config, error) {\\n\\tif cfg := leaderelection.GetConfig(ctx); cfg != nil {\\n\\t\\treturn cfg, nil\\n\\t}\\n\\n\\tleaderElectionConfigMap, err := kubeclient.Get(ctx).CoreV1().ConfigMaps(system.Namespace()).Get(ctx, leaderelection.ConfigMapName(), metav1.GetOptions{})\\n\\tif apierrors.IsNotFound(err) {\\n\\t\\treturn leaderelection.NewConfigFromConfigMap(nil)\\n\\t} else if err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\treturn leaderelection.NewConfigFromConfigMap(leaderElectionConfigMap)\\n}\\n\\n// GetObservabilityConfig gets the observability config from the (in order):\\n// 1. provided context,\\n// 2. reading from the API server,\\n// 3. defaults (if not found).\\nfunc GetObservabilityConfig(ctx context.Context) (*observability.Config, error) {\\n\\tif cfg := observability.GetConfig(ctx); cfg != nil {\\n\\t\\treturn cfg, nil\\n\\t}\\n\\n\\tclient := kubeclient.Get(ctx).CoreV1().ConfigMaps(system.Namespace())\\n\\tcm, err := client.Get(ctx, o11yconfigmap.Name(), metav1.GetOptions{})\\n\\n\\tif apierrors.IsNotFound(err) {\\n\\t\\treturn observability.DefaultConfig(), nil\\n\\t} else if err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\n\\treturn o11yconfigmap.Parse(cm)\\n}\\n\\n// EnableInjectionOrDie enables Knative Injection and starts the informers.\\n// Both Context and Config are optional.\\n// Deprecated: use injection.EnableInjectionOrDie\\nfunc EnableInjectionOrDie(ctx context.Context, cfg *rest.Config) context.Context {\\n\\tctx, startInformers := injection.EnableInjectionOrDie(ctx, cfg)\\n\\tgo startInformers()\\n\\treturn ctx\\n}\\n\\n// Main runs the generic main flow with a new context.\\n// If any of the constructed controllers are AdmissionControllers or Conversion\\n// webhooks, then a webhook is started to serve them.\\nfunc Main(component string, ctors ...injection.ControllerConstructor) {\\n\\t// Set up signals so we handle the first shutdown signal gracefully.\\n\\tMainWithContext(signals.NewContext(), component, ctors...)\\n}\\n\\n// Legacy aliases for back-compat.\\nvar (\\n\\tWebhookMainWithContext = MainWithContext\\n\\tWebhookMainWithConfig  = MainWithConfig\\n)\\n\\n// MainNamed runs the generic main flow for controllers and webhooks.\\n//\\n// In addition to the MainWithConfig flow, it defines a `disabled-controllers` flag that allows disabling controllers\\n// by name.\\nfunc MainNamed(ctx context.Context, component string, ctors ...injection.NamedControllerConstructor) {\\n\\tdisabledControllers := flag.String(\\\"disable-controllers\\\", \\\"\\\", \\\"Comma-separated list of disabled controllers.\\\")\\n\\n\\t// HACK: This parses flags, so the above should be set once this runs.\\n\\tcfg := injection.ParseAndGetRESTConfigOrDie()\\n\\n\\tenabledCtors := enabledControllers(strings.Split(*disabledControllers, \\\",\\\"), ctors)\\n\\tMainWithConfig(ctx, component, cfg, toControllerConstructors(enabledCtors)...)\\n}\\n\\nfunc enabledControllers(disabledControllers []string, ctors []injection.NamedControllerConstructor) []injection.NamedControllerConstructor {\\n\\tdisabledControllersSet := sets.NewString(disabledControllers...)\\n\\tactiveCtors := make([]injection.NamedControllerConstructor, 0, len(ctors))\\n\\tfor _, ctor := range ctors {\\n\\t\\tif disabledControllersSet.Has(ctor.Name) {\\n\\t\\t\\tlog.Printf(\\\"Disabling controller %s\\\", ctor.Name)\\n\\t\\t\\tcontinue\\n\\t\\t}\\n\\t\\tactiveCtors = append(activeCtors, ctor)\\n\\t}\\n\\treturn activeCtors\\n}\\n\\nfunc toControllerConstructors(namedCtors []injection.NamedControllerConstructor) []injection.ControllerConstructor {\\n\\tctors := make([]injection.ControllerConstructor, 0, len(namedCtors))\\n\\tfor _, ctor := range namedCtors {\\n\\t\\tctors = append(ctors, ctor.ControllerConstructor)\\n\\t}\\n\\treturn ctors\\n}\\n\\n// MainWithContext runs the generic main flow for controllers and\\n// webhooks. Use MainWithContext if you do not need to serve webhooks.\\nfunc MainWithContext(ctx context.Context, component string, ctors ...injection.ControllerConstructor) {\\n\\t// Allow configuration of threads per controller\\n\\tif val, ok := os.LookupEnv(\\\"K_THREADS_PER_CONTROLLER\\\"); ok {\\n\\t\\tthreadsPerController, err := strconv.Atoi(val)\\n\\t\\tif err != nil {\\n\\t\\t\\tlog.Fatalf(\\\"failed to parse value %q of K_THREADS_PER_CONTROLLER: %v\\\\n\\\", val, err)\\n\\t\\t}\\n\\t\\tcontroller.DefaultThreadsPerController = threadsPerController\\n\\t}\\n\\n\\t// TODO(mattmoor): Remove this once HA is stable.\\n\\tdisableHighAvailability := flag.Bool(\\\"disable-ha\\\", false,\\n\\t\\t\\\"Whether to disable high-availability functionality for this component.  This flag will be deprecated \\\"+\\n\\t\\t\\t\\\"and removed when we have promoted this feature to stable, so do not pass it without filing an \\\"+\\n\\t\\t\\t\\\"issue upstream!\\\")\\n\\n\\t// HACK: This parses flags, so the above should be set once this runs.\\n\\tcfg := injection.ParseAndGetRESTConfigOrDie()\\n\\n\\tif *disableHighAvailability {\\n\\t\\tctx = WithHADisabled(ctx)\\n\\t}\\n\\n\\tMainWithConfig(ctx, component, cfg, ctors...)\\n}\\n\\ntype haDisabledKey struct{}\\n\\n// WithHADisabled signals to MainWithConfig that it should not set up an appropriate leader elector for this component.\\nfunc WithHADisabled(ctx context.Context) context.Context {\\n\\treturn context.WithValue(ctx, haDisabledKey{}, struct{}{})\\n}\\n\\n// IsHADisabled checks the context for the desired to disabled leader elector.\\nfunc IsHADisabled(ctx context.Context) bool {\\n\\treturn ctx.Value(haDisabledKey{}) != nil\\n}\\n\\n// MainWithConfig runs the generic main flow for controllers and webhooks\\n// with the given config.\\nfunc MainWithConfig(ctx context.Context, component string, cfg *rest.Config, ctors ...injection.ControllerConstructor) {\\n\\tlog.Printf(\\\"Registering %d clients\\\", len(injection.Default.GetClients()))\\n\\tlog.Printf(\\\"Registering %d informer factories\\\", len(injection.Default.GetInformerFactories()))\\n\\tlog.Printf(\\\"Registering %d informers\\\", len(injection.Default.GetInformers()))\\n\\tlog.Printf(\\\"Registering %d controllers\\\", len(ctors))\\n\\n\\t// Respect user provided settings, but if omitted customize the default behavior.\\n\\tif cfg.QPS == 0 {\\n\\t\\t// Adjust our client's rate limits based on the number of controllers we are running.\\n\\t\\tcfg.QPS = float32(len(ctors)) * rest.DefaultQPS\\n\\t}\\n\\tif cfg.Burst == 0 {\\n\\t\\tcfg.Burst = len(ctors) * rest.DefaultBurst\\n\\t}\\n\\n\\tctx, startInformers := injection.EnableInjectionOrDie(ctx, cfg)\\n\\n\\tlogger, atomicLevel := SetupLoggerOrDie(ctx, component)\\n\\tdefer logger.Sync()\\n\\tctx = logging.WithLogger(ctx, logger)\\n\\n\\tklog.SetLogger(zapr.NewLogger(logger.Desugar()))\\n\\n\\t// Override client-go's warning handler to give us nicely printed warnings.\\n\\trest.SetDefaultWarningHandler(\\u0026logging.WarningHandler{Logger: logger})\\n\\n\\tpprof := k8sruntime.NewProfilingServer(logger.Named(\\\"pprof\\\"))\\n\\n\\tCheckK8sClientMinimumVersionOrDie(ctx, logger)\\n\\tcmw := SetupConfigMapWatchOrDie(ctx, logger)\\n\\n\\t// Set up leader election config\\n\\tleaderElectionConfig, err := GetLeaderElectionConfig(ctx)\\n\\tif err != nil {\\n\\t\\tlogger.Fatal(\\\"Error loading leader election configuration: \\\", err)\\n\\t}\\n\\n\\tif !IsHADisabled(ctx) {\\n\\t\\t// Signal that we are executing in a context with leader election.\\n\\t\\tctx = leaderelection.WithDynamicLeaderElectorBuilder(ctx, kubeclient.Get(ctx),\\n\\t\\t\\tleaderElectionConfig.GetComponentConfig(component))\\n\\t}\\n\\n\\tmp, tp := SetupObservabilityOrDie(ctx, component, logger, pprof)\\n\\tdefer func() {\\n\\t\\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\\n\\t\\tdefer cancel()\\n\\n\\t\\tif err := mp.Shutdown(ctx); err != nil {\\n\\t\\t\\tlogger.Errorw(\\\"Error flushing metrics\\\", zap.Error(err))\\n\\t\\t}\\n\\t\\tif err := tp.Shutdown(ctx); err != nil {\\n\\t\\t\\tlogger.Errorw(\\\"Error flushing traces\\\", zap.Error(err))\\n\\t\\t}\\n\\t}()\\n\\n\\tcontrollers, webhooks := ControllersAndWebhooksFromCtors(ctx, cmw, ctors...)\\n\\tWatchLoggingConfigOrDie(ctx, cmw, logger, atomicLevel, component)\\n\\tWatchObservabilityConfigOrDie(ctx, cmw, pprof, logger, component)\\n\\n\\teg, egCtx := errgroup.WithContext(ctx)\\n\\teg.Go(pprof.ListenAndServe)\\n\\n\\t// Many of the webhooks rely on configuration, e.g. configurable defaults, feature flags.\\n\\t// So make sure that we have synchronized our configuration state before launching the\\n\\t// webhooks, so that things are properly initialized.\\n\\tlogger.Info(\\\"Starting configmap watcher...\\\")\\n\\tif err := cmw.Start(ctx.Done()); err != nil {\\n\\t\\tlogger.Fatalw(\\\"Failed to start configmap watcher\\\", zap.Error(err))\\n\\t}\\n\\n\\t// If we have one or more admission controllers, then start the webhook\\n\\t// and pass them in.\\n\\tvar wh *webhook.Webhook\\n\\tif len(webhooks) \\u003e 0 {\\n\\t\\twh, err = webhook.New(ctx, webhooks)\\n\\t\\tif err != nil {\\n\\t\\t\\tlogger.Fatalw(\\\"Failed to create webhook\\\", zap.Error(err))\\n\\t\\t}\\n\\t\\teg.Go(func() error {\\n\\t\\t\\treturn wh.Run(ctx.Done())\\n\\t\\t})\\n\\t}\\n\\n\\t// Start the injection clients and informers.\\n\\tstartInformers()\\n\\n\\t// Wait for webhook informers to sync.\\n\\tif wh != nil {\\n\\t\\twh.InformersHaveSynced()\\n\\t}\\n\\tlogger.Info(\\\"Starting controllers...\\\")\\n\\teg.Go(func() error {\\n\\t\\treturn controller.StartAll(ctx, controllers...)\\n\\t})\\n\\n\\t// Setup default health checks to catch issues with cache sync etc.\\n\\tif !healthProbesDisabled(ctx) {\\n\\t\\teg.Go(func() error {\\n\\t\\t\\treturn injection.ServeHealthProbes(ctx, injection.HealthCheckDefaultPort)\\n\\t\\t})\\n\\t}\\n\\n\\t// This will block until either a signal arrives or one of the grouped functions\\n\\t// returns an error.\\n\\t\\u003c-egCtx.Done()\\n\\n\\tpprof.Shutdown(context.Background())\\n\\n\\t// Don't forward ErrServerClosed as that indicates we're already shutting down.\\n\\tif err := eg.Wait(); err != nil \\u0026\\u0026 !errors.Is(err, http.ErrServerClosed) {\\n\\t\\tlogger.Errorw(\\\"Error while running server\\\", zap.Error(err))\\n\\t}\\n}\\n\\ntype healthProbesDisabledKey struct{}\\n\\n// WithHealthProbesDisabled signals to MainWithContext that it should disable default probes (readiness and liveness).\\nfunc WithHealthProbesDisabled(ctx context.Context) context.Context {\\n\\treturn context.WithValue(ctx, healthProbesDisabledKey{}, struct{}{})\\n}\\n\\nfunc healthProbesDisabled(ctx context.Context) bool {\\n\\treturn ctx.Value(healthProbesDisabledKey{}) != nil\\n}\\n\\n// SetupLoggerOrDie sets up the logger using the config from the given context\\n// and returns a logger and atomic level, or dies by calling log.Fatalf.\\nfunc SetupLoggerOrDie(ctx context.Context, component string) (*zap.SugaredLogger, zap.AtomicLevel) {\\n\\tloggingConfig, err := GetLoggingConfig(ctx)\\n\\tif err != nil {\\n\\t\\tlog.Fatal(\\\"Error reading/parsing logging configuration: \\\", err)\\n\\t}\\n\\tl, level := logging.NewLoggerFromConfig(loggingConfig, component)\\n\\n\\tif pn := system.PodName(); pn != \\\"\\\" {\\n\\t\\tl = l.With(zap.String(logkey.Pod, pn))\\n\\t}\\n\\n\\treturn l, level\\n}\\n\\n// SetupObservabilityOrDie sets up the observability using the config from the given context\\n// or dies by calling log.Fatalf.\\nfunc SetupObservabilityOrDie(\\n\\tctx context.Context,\\n\\tcomponent string,\\n\\tlogger *zap.SugaredLogger,\\n\\tpprof *k8sruntime.ProfilingServer,\\n) (*metrics.MeterProvider, *tracing.TracerProvider) {\\n\\tcfg, err := GetObservabilityConfig(ctx)\\n\\tif err != nil {\\n\\t\\tlogger.Fatal(\\\"Error loading observability configuration: \\\", err)\\n\\t}\\n\\n\\tpprof.UpdateFromConfig(cfg.Runtime)\\n\\n\\tresource := resource.Default(component)\\n\\n\\tmeterProvider, err := metrics.NewMeterProvider(\\n\\t\\tctx,\\n\\t\\tcfg.Metrics,\\n\\t\\tmetric.WithView(OTelViews(ctx)...),\\n\\t\\tmetric.WithResource(resource),\\n\\t)\\n\\tif err != nil {\\n\\t\\tlogger.Fatalw(\\\"Failed to setup meter provider\\\", zap.Error(err))\\n\\t}\\n\\n\\totel.SetMeterProvider(meterProvider)\\n\\n\\tworkQueueMetrics, err := k8smetrics.NewWorkqueueMetricsProvider(\\n\\t\\tk8smetrics.WithMeterProvider(meterProvider),\\n\\t)\\n\\tif err != nil {\\n\\t\\tlogger.Fatalw(\\\"Failed to setup k8s workqueue metrics\\\", zap.Error(err))\\n\\t}\\n\\n\\tworkqueue.SetProvider(workQueueMetrics)\\n\\tcontroller.SetMetricsProvider(workQueueMetrics)\\n\\n\\tclientMetrics, err := k8smetrics.NewClientMetricProvider(\\n\\t\\tk8smetrics.WithMeterProvider(meterProvider),\\n\\t)\\n\\tif err != nil {\\n\\t\\tlogger.Fatalw(\\\"Failed to setup k8s client go metrics\\\", zap.Error(err))\\n\\t}\\n\\n\\tk8stoolmetrics.Register(k8stoolmetrics.RegisterOpts{\\n\\t\\tRequestLatency: clientMetrics.RequestLatencyMetric(),\\n\\t\\tRequestResult:  clientMetrics.RequestResultMetric(),\\n\\t})\\n\\n\\terr = runtime.Start(\\n\\t\\truntime.WithMinimumReadMemStatsInterval(cfg.Runtime.ExportInterval),\\n\\t)\\n\\tif err != nil {\\n\\t\\tlogger.Fatalw(\\\"Failed to start runtime metrics\\\", zap.Error(err))\\n\\t}\\n\\n\\ttracerProvider, err := tracing.NewTracerProvider(\\n\\t\\tctx,\\n\\t\\tcfg.Tracing,\\n\\t\\ttrace.WithResource(resource),\\n\\t)\\n\\tif err != nil {\\n\\t\\tlogger.Fatalw(\\\"Failed to setup trace provider\\\", zap.Error(err))\\n\\t}\\n\\n\\totel.SetTextMapPropagator(tracing.DefaultTextMapPropagator())\\n\\totel.SetTracerProvider(tracerProvider)\\n\\n\\treturn meterProvider, tracerProvider\\n}\\n\\n// CheckK8sClientMinimumVersionOrDie checks that the hosting Kubernetes cluster\\n// is at least the minimum allowable version or dies by calling log.Fatalw.\\nfunc CheckK8sClientMinimumVersionOrDie(ctx context.Context, logger *zap.SugaredLogger) {\\n\\tkc := kubeclient.Get(ctx)\\n\\tif err := version.CheckMinimumVersion(kc.Discovery()); err != nil {\\n\\t\\tlogger.Fatalw(\\\"Version check failed\\\", zap.Error(err))\\n\\t}\\n}\\n\\n// SetupConfigMapWatchOrDie establishes a watch of the configmaps in the system\\n// namespace that are labeled to be watched or dies by calling log.Fatalw.\\nfunc SetupConfigMapWatchOrDie(ctx context.Context, logger *zap.SugaredLogger) *cminformer.InformedWatcher {\\n\\tkc := kubeclient.Get(ctx)\\n\\t// Create ConfigMaps watcher with optional label-based filter.\\n\\tvar cmLabelReqs []labels.Requirement\\n\\tif cmLabel := system.ResourceLabel(); cmLabel != \\\"\\\" {\\n\\t\\treq, err := cminformer.FilterConfigByLabelExists(cmLabel)\\n\\t\\tif err != nil {\\n\\t\\t\\tlogger.Fatalw(\\\"Failed to generate requirement for label \\\"+cmLabel, zap.Error(err))\\n\\t\\t}\\n\\t\\tlogger.Info(\\\"Setting up ConfigMap watcher with label selector: \\\", req)\\n\\t\\tcmLabelReqs = append(cmLabelReqs, *req)\\n\\t}\\n\\t// TODO(mattmoor): This should itself take a context and be injection-based.\\n\\treturn cminformer.NewInformedWatcher(kc, system.Namespace(), cmLabelReqs...)\\n}\\n\\n// WatchLoggingConfigOrDie establishes a watch of the logging config or dies by\\n// calling log.Fatalw. Note, if the config does not exist, it will be defaulted\\n// and this method will not die.\\nfunc WatchLoggingConfigOrDie(ctx context.Context, cmw *cminformer.InformedWatcher, logger *zap.SugaredLogger, atomicLevel zap.AtomicLevel, component string) {\\n\\tif _, err := kubeclient.Get(ctx).CoreV1().ConfigMaps(system.Namespace()).Get(ctx, logging.ConfigMapName(),\\n\\t\\tmetav1.GetOptions{}); err == nil {\\n\\t\\tcmw.Watch(logging.ConfigMapName(), logging.UpdateLevelFromConfigMap(logger, atomicLevel, component))\\n\\t} else if !apierrors.IsNotFound(err) {\\n\\t\\tlogger.Fatalw(\\\"Error reading ConfigMap \\\"+logging.ConfigMapName(), zap.Error(err))\\n\\t}\\n}\\n\\n// WatchObservabilityConfigOrDie establishes a watch of the observability config\\n// or dies by calling log.Fatalw. Note, if the config does not exist, it will be\\n// defaulted and this method will not die.\\nfunc WatchObservabilityConfigOrDie(\\n\\tctx context.Context,\\n\\tcmw *cminformer.InformedWatcher,\\n\\tpprof *k8sruntime.ProfilingServer,\\n\\tlogger *zap.SugaredLogger,\\n\\tcomponent string,\\n) {\\n\\tcmName := o11yconfigmap.Name()\\n\\tclient := kubeclient.Get(ctx).CoreV1().ConfigMaps(system.Namespace())\\n\\n\\tobservers := []configmap.Observer{\\n\\t\\tpprof.UpdateFromConfigMap,\\n\\t}\\n\\n\\tif _, err := client.Get(ctx, cmName, metav1.GetOptions{}); err == nil {\\n\\t\\tcmw.Watch(cmName, observers...)\\n\\t} else if !apierrors.IsNotFound(err) {\\n\\t\\tlogger.Fatalw(\\\"Error reading ConfigMap \\\"+cmName, zap.Error(err))\\n\\t}\\n}\\n\\n// ControllersAndWebhooksFromCtors returns a list of the controllers and a list\\n// of the webhooks created from the given constructors.\\nfunc ControllersAndWebhooksFromCtors(ctx context.Context,\\n\\tcmw *cminformer.InformedWatcher,\\n\\tctors ...injection.ControllerConstructor,\\n) ([]*controller.Impl, []any) {\\n\\t// Check whether the context has been infused with a leader elector builder.\\n\\t// If it has, then every reconciler we plan to start MUST implement LeaderAware.\\n\\tleEnabled := leaderelection.HasLeaderElection(ctx)\\n\\n\\tcontrollers := make([]*controller.Impl, 0, len(ctors))\\n\\twebhooks := make([]any, 0)\\n\\tfor _, cf := range ctors {\\n\\t\\tctrl := cf(ctx, cmw)\\n\\t\\tcontrollers = append(controllers, ctrl)\\n\\n\\t\\t// Build a list of any reconcilers that implement webhook.AdmissionController\\n\\t\\tswitch c := ctrl.Reconciler.(type) {\\n\\t\\tcase webhook.AdmissionController, webhook.ConversionController:\\n\\t\\t\\twebhooks = append(webhooks, c)\\n\\t\\t}\\n\\n\\t\\tif leEnabled {\\n\\t\\t\\tif _, ok := ctrl.Reconciler.(reconciler.LeaderAware); !ok {\\n\\t\\t\\t\\tlog.Fatalf(\\\"%T is not leader-aware, all reconcilers must be leader-aware to enable fine-grained leader election.\\\", ctrl.Reconciler)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n\\n\\treturn controllers, webhooks\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(component string, ctors ...injection.ControllerConstructor) {\",\"lineNumber\":154,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/knative/pkg\"},\"file\":{\"path\":\"vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"url\":\"/github.com/knative/pkg/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"content\":\"// Copyright 2017 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//    http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\npackage gnostic_extension_v1\\n\\nimport (\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"google.golang.org/protobuf/proto\\\"\\n\\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n)\\n\\ntype extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n\\n// Main implements the main program of an extension handler.\\nfunc Main(handler extensionHandler) {\\n\\t// unpack the request\\n\\tdata, err := ioutil.ReadAll(os.Stdin)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\tif len(data) == 0 {\\n\\t\\tlog.Println(\\\"No input data.\\\")\\n\\t\\tos.Exit(1)\\n\\t}\\n\\trequest := \\u0026ExtensionHandlerRequest{}\\n\\terr = proto.Unmarshal(data, request)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Input error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\t// call the handler\\n\\thandled, output, err := handler(request.Wrapper.ExtensionName, request.Wrapper.Yaml)\\n\\t// respond with the output of the handler\\n\\tresponse := \\u0026ExtensionHandlerResponse{\\n\\t\\tHandled: false, // default assumption\\n\\t\\tErrors:  make([]string, 0),\\n\\t}\\n\\tif err != nil {\\n\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t} else if handled {\\n\\t\\tresponse.Handled = true\\n\\t\\tresponse.Value, err = anypb.New(output)\\n\\t\\tif err != nil {\\n\\t\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t\\t}\\n\\t}\\n\\tresponseBytes, _ := proto.Marshal(response)\\n\\tos.Stdout.Write(responseBytes)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(handler extensionHandler) {\",\"lineNumber\":28,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/openshift/cluster-authentication-operator\"},\"file\":{\"path\":\"vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"url\":\"/github.com/openshift/cluster-authentication-operator/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\",\"content\":\"// Copyright 2017 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//    http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\npackage gnostic_extension_v1\\n\\nimport (\\n\\t\\\"io/ioutil\\\"\\n\\t\\\"log\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"google.golang.org/protobuf/proto\\\"\\n\\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n)\\n\\ntype extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n\\n// Main implements the main program of an extension handler.\\nfunc Main(handler extensionHandler) {\\n\\t// unpack the request\\n\\tdata, err := ioutil.ReadAll(os.Stdin)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\tif len(data) == 0 {\\n\\t\\tlog.Println(\\\"No input data.\\\")\\n\\t\\tos.Exit(1)\\n\\t}\\n\\trequest := \\u0026ExtensionHandlerRequest{}\\n\\terr = proto.Unmarshal(data, request)\\n\\tif err != nil {\\n\\t\\tlog.Println(\\\"Input error:\\\", err.Error())\\n\\t\\tos.Exit(1)\\n\\t}\\n\\t// call the handler\\n\\thandled, output, err := handler(request.Wrapper.ExtensionName, request.Wrapper.Yaml)\\n\\t// respond with the output of the handler\\n\\tresponse := \\u0026ExtensionHandlerResponse{\\n\\t\\tHandled: false, // default assumption\\n\\t\\tErrors:  make([]string, 0),\\n\\t}\\n\\tif err != nil {\\n\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t} else if handled {\\n\\t\\tresponse.Handled = true\\n\\t\\tresponse.Value, err = anypb.New(output)\\n\\t\\tif err != nil {\\n\\t\\t\\tresponse.Errors = append(response.Errors, err.Error())\\n\\t\\t}\\n\\t}\\n\\tresponseBytes, _ := proto.Marshal(response)\\n\\tos.Stdout.Write(responseBytes)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(handler extensionHandler) {\",\"lineNumber\":28,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/google/trillian-examples\"},\"file\":{\"path\":\"binary_transparency/firmware/cmd/publisher/impl/publish.go\",\"url\":\"/github.com/google/trillian-examples/-/blob/binary_transparency/firmware/cmd/publisher/impl/publish.go\",\"content\":\"// Copyright 2020 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//     http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\n// Package impl is a the implementation of a tool to put firmware metadata into the log.\\npackage impl\\n\\nimport (\\n\\t\\\"context\\\"\\n\\t\\\"crypto/sha512\\\"\\n\\t\\\"encoding/json\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"net/url\\\"\\n\\t\\\"os\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/golang/glog\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/api\\\"\\n\\tdummy_common \\\"github.com/google/trillian-examples/binary_transparency/firmware/devices/dummy/common\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/devices/usbarmory\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/internal/client\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/internal/crypto\\\"\\n\\t\\\"golang.org/x/mod/sumdb/note\\\"\\n)\\n\\n// PublishOpts encapsulates parameters for the publish Main below.\\ntype PublishOpts struct {\\n\\tLogURL         string\\n\\tLogSigVerifier note.Verifier\\n\\tDeviceID       string\\n\\tRevision       uint64\\n\\tBinaryPath     string\\n\\tTimestamp      string\\n\\tOutputPath     string\\n}\\n\\n// Main is the entrypoint for the implementation of the publisher.\\nfunc Main(ctx context.Context, opts PublishOpts) error {\\n\\tlogURL, err := url.Parse(opts.LogURL)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"LogURL is invalid: %w\\\", err)\\n\\t}\\n\\n\\tmetadata, fw, err := createManifest(opts)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to create manifest: %w\\\", err)\\n\\t}\\n\\n\\tglog.Infof(\\\"Measurement: %x\\\", metadata.ExpectedFirmwareMeasurement)\\n\\n\\tjs, err := createStatementJSON(metadata)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to marshal statement: %w\\\", err)\\n\\t}\\n\\n\\tc := \\u0026client.SubmitClient{\\n\\t\\tReadonlyClient: \\u0026client.ReadonlyClient{\\n\\t\\t\\tLogURL:         logURL,\\n\\t\\t\\tLogSigVerifier: opts.LogSigVerifier,\\n\\t\\t},\\n\\t}\\n\\n\\tinitialCP, err := c.GetCheckpoint()\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to get a pre-submission checkpoint from log: %w\\\", err)\\n\\t}\\n\\n\\tglog.Info(\\\"Submitting entry...\\\")\\n\\tif err := c.PublishFirmware(js, fw); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"couldn't submit statement: %w\\\", err)\\n\\t}\\n\\n\\tglog.Info(\\\"Successfully submitted entry, waiting for inclusion...\\\")\\n\\tcp, consistency, ip, err := client.AwaitInclusion(ctx, c.ReadonlyClient, *initialCP, js)\\n\\tif err != nil {\\n\\t\\tglog.Errorf(\\\"Failed while waiting for inclusion: %v\\\", err)\\n\\t\\tglog.Warningf(\\\"Failed checkpoint: %s\\\", cp)\\n\\t\\tglog.Warningf(\\\"Failed consistency proof: %x\\\", consistency)\\n\\t\\tglog.Warningf(\\\"Failed inclusion proof: %x\\\", ip)\\n\\t\\treturn fmt.Errorf(\\\"bailing: %w\\\", err)\\n\\t}\\n\\n\\tglog.Infof(\\\"Successfully logged %s\\\", js)\\n\\n\\tif len(opts.OutputPath) \\u003e 0 {\\n\\t\\tglog.Infof(\\\"Creating update package file %q...\\\", opts.OutputPath)\\n\\t\\tpb, err := json.Marshal(\\n\\t\\t\\tapi.ProofBundle{\\n\\t\\t\\t\\tManifestStatement: js,\\n\\t\\t\\t\\tCheckpoint:        cp.Envelope,\\n\\t\\t\\t\\tInclusionProof:    ip,\\n\\t\\t\\t})\\n\\t\\tif err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to marshal ProofBundle: %w\\\", err)\\n\\t\\t}\\n\\n\\t\\tbundle := api.UpdatePackage{\\n\\t\\t\\tFirmwareImage: fw,\\n\\t\\t\\tProofBundle:   pb,\\n\\t\\t}\\n\\n\\t\\tf, err := os.OpenFile(opts.OutputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to create output package file %q: %w\\\", opts.OutputPath, err)\\n\\t\\t}\\n\\t\\tdefer func() {\\n\\t\\t\\tif err := f.Close(); err != nil {\\n\\t\\t\\t\\tglog.Errorf(\\\"f.Close(): %v\\\", err)\\n\\t\\t\\t}\\n\\t\\t}()\\n\\n\\t\\tif err := json.NewEncoder(f).Encode(bundle); err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to encode output package JSON: %w\\\", err)\\n\\t\\t}\\n\\t\\tglog.Infof(\\\"Successfully created update package file %q\\\", opts.OutputPath)\\n\\t}\\n\\treturn nil\\n}\\n\\nfunc createManifest(opts PublishOpts) (api.FirmwareMetadata, []byte, error) {\\n\\tvar measure func([]byte) ([]byte, error)\\n\\tswitch opts.DeviceID {\\n\\tcase \\\"armory\\\":\\n\\t\\tmeasure = usbarmory.ExpectedMeasurement\\n\\tcase \\\"dummy\\\":\\n\\t\\tmeasure = dummy_common.ExpectedMeasurement\\n\\tdefault:\\n\\t\\treturn api.FirmwareMetadata{}, nil, errors.New(\\\"DeviceID must be one of: 'dummy', 'armory'\\\")\\n\\t}\\n\\n\\tfw, err := os.ReadFile(opts.BinaryPath)\\n\\tif err != nil {\\n\\t\\treturn api.FirmwareMetadata{}, nil, fmt.Errorf(\\\"failed to read %q: %w\\\", opts.BinaryPath, err)\\n\\t}\\n\\n\\th := sha512.Sum512(fw)\\n\\n\\tm, err := measure(fw)\\n\\tif err != nil {\\n\\t\\treturn api.FirmwareMetadata{}, nil, fmt.Errorf(\\\"failed to calculate expected measurement for firmware: %w\\\", err)\\n\\t}\\n\\n\\tbuildTime := opts.Timestamp\\n\\tif buildTime == \\\"\\\" {\\n\\t\\tbuildTime = time.Now().Format(time.RFC3339)\\n\\t}\\n\\tmetadata := api.FirmwareMetadata{\\n\\t\\tDeviceID:                    opts.DeviceID,\\n\\t\\tFirmwareRevision:            opts.Revision,\\n\\t\\tFirmwareImageSHA512:         h[:],\\n\\t\\tExpectedFirmwareMeasurement: m,\\n\\t\\tBuildTimestamp:              buildTime,\\n\\t}\\n\\n\\treturn metadata, fw, nil\\n}\\n\\nfunc createStatementJSON(m api.FirmwareMetadata) ([]byte, error) {\\n\\tjs, err := json.Marshal(m)\\n\\tif err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"failed to marshal metadata: %w\\\", err)\\n\\t}\\n\\tsig, err := crypto.Publisher.SignMessage(api.FirmwareMetadataType, js)\\n\\tif err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"failed to generate signature: %w\\\", err)\\n\\t}\\n\\n\\tstatement := api.SignedStatement{\\n\\t\\tType:      api.FirmwareMetadataType,\\n\\t\\tStatement: js,\\n\\t\\tSignature: sig,\\n\\t}\\n\\n\\treturn json.Marshal(statement)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(ctx context.Context, opts PublishOpts) error {\",\"lineNumber\":48,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/bnb-chain/opbnb\"},\"file\":{\"path\":\"op-heartbeat/service.go\",\"url\":\"/github.com/bnb-chain/opbnb/-/blob/op-heartbeat/service.go\",\"content\":\"package op_heartbeat\\n\\nimport (\\n\\t\\\"context\\\"\\n\\t\\\"encoding/json\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"io\\\"\\n\\t\\\"net\\\"\\n\\t\\\"net/http\\\"\\n\\t\\\"os\\\"\\n\\t\\\"os/signal\\\"\\n\\t\\\"strconv\\\"\\n\\t\\\"strings\\\"\\n\\t\\\"syscall\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/urfave/cli/v2\\\"\\n\\n\\t\\\"github.com/ethereum/go-ethereum/log\\\"\\n\\n\\t\\\"github.com/ethereum-optimism/optimism/op-node/heartbeat\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-service/httputil\\\"\\n\\toplog \\\"github.com/ethereum-optimism/optimism/op-service/log\\\"\\n\\topmetrics \\\"github.com/ethereum-optimism/optimism/op-service/metrics\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-service/oppprof\\\"\\n)\\n\\nconst (\\n\\tHTTPMaxHeaderSize = 10 * 1024\\n\\tHTTPMaxBodySize   = 1024 * 1024\\n)\\n\\nfunc Main(version string) func(ctx *cli.Context) error {\\n\\treturn func(cliCtx *cli.Context) error {\\n\\t\\tcfg := NewConfig(cliCtx)\\n\\t\\tif err := cfg.Check(); err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"invalid CLI flags: %w\\\", err)\\n\\t\\t}\\n\\n\\t\\tl := oplog.NewLogger(oplog.AppOut(cliCtx), cfg.Log)\\n\\t\\toplog.SetGlobalLogHandler(l.Handler())\\n\\t\\tl.Info(\\\"starting heartbeat monitor\\\", \\\"version\\\", version)\\n\\n\\t\\tsrv, err := Start(cliCtx.Context, l, cfg, version)\\n\\t\\tif err != nil {\\n\\t\\t\\tl.Crit(\\\"error starting application\\\", \\\"err\\\", err)\\n\\t\\t}\\n\\n\\t\\tdoneCh := make(chan os.Signal, 1)\\n\\t\\tsignal.Notify(doneCh, []os.Signal{\\n\\t\\t\\tos.Interrupt,\\n\\t\\t\\tos.Kill,\\n\\t\\t\\tsyscall.SIGTERM,\\n\\t\\t\\tsyscall.SIGQUIT,\\n\\t\\t}...)\\n\\t\\t\\u003c-doneCh\\n\\t\\treturn srv.Stop(context.Background())\\n\\t}\\n}\\n\\ntype HeartbeatService struct {\\n\\tmetrics, http *httputil.HTTPServer\\n\\tpprofService  *oppprof.Service\\n}\\n\\nfunc (hs *HeartbeatService) Stop(ctx context.Context) error {\\n\\tvar result error\\n\\tif hs.pprofService != nil {\\n\\t\\tresult = errors.Join(result, hs.pprofService.Stop(ctx))\\n\\t}\\n\\tif hs.metrics != nil {\\n\\t\\tresult = errors.Join(result, hs.metrics.Stop(ctx))\\n\\t}\\n\\tif hs.http != nil {\\n\\t\\tresult = errors.Join(result, hs.http.Stop(ctx))\\n\\t}\\n\\treturn result\\n}\\n\\nfunc Start(ctx context.Context, l log.Logger, cfg Config, version string) (*HeartbeatService, error) {\\n\\ths := \\u0026HeartbeatService{}\\n\\n\\tregistry := opmetrics.NewRegistry()\\n\\tmetricsCfg := cfg.Metrics\\n\\tif metricsCfg.Enabled {\\n\\t\\tl.Debug(\\\"starting metrics server\\\", \\\"addr\\\", metricsCfg.ListenAddr, \\\"port\\\", metricsCfg.ListenPort)\\n\\t\\tmetricsSrv, err := opmetrics.StartServer(registry, metricsCfg.ListenAddr, metricsCfg.ListenPort)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn nil, errors.Join(fmt.Errorf(\\\"failed to start metrics server: %w\\\", err), hs.Stop(ctx))\\n\\t\\t}\\n\\t\\ths.metrics = metricsSrv\\n\\t\\tl.Info(\\\"started metrics server\\\", \\\"addr\\\", metricsSrv.Addr())\\n\\t}\\n\\n\\tpprofCfg := cfg.Pprof\\n\\ths.pprofService = oppprof.New(\\n\\t\\tpprofCfg.ListenEnabled,\\n\\t\\tpprofCfg.ListenAddr,\\n\\t\\tpprofCfg.ListenPort,\\n\\t\\tpprofCfg.ProfileType,\\n\\t\\tpprofCfg.ProfileDir,\\n\\t\\tpprofCfg.ProfileFilename,\\n\\t)\\n\\n\\tif err := hs.pprofService.Start(); err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"failed to start pprof service: %w\\\", err)\\n\\t}\\n\\n\\tmetrics := NewMetrics(registry)\\n\\tmetrics.RecordVersion(version)\\n\\tmux := http.NewServeMux()\\n\\tmux.HandleFunc(\\\"/healthz\\\", HealthzHandler)\\n\\tmux.Handle(\\\"/\\\", Handler(l, metrics))\\n\\trecorder := opmetrics.NewPromHTTPRecorder(registry, MetricsNamespace)\\n\\tmw := opmetrics.NewHTTPRecordingMiddleware(recorder, mux)\\n\\n\\tsrv, err := httputil.StartHTTPServer(\\n\\t\\tnet.JoinHostPort(cfg.HTTPAddr, strconv.Itoa(cfg.HTTPPort)),\\n\\t\\tmw,\\n\\t\\thttputil.WithTimeouts(httputil.HTTPTimeouts{\\n\\t\\t\\tReadTimeout:       30 * time.Second,\\n\\t\\t\\tReadHeaderTimeout: 30 * time.Second,\\n\\t\\t\\tWriteTimeout:      30 * time.Second,\\n\\t\\t\\tIdleTimeout:       time.Minute,\\n\\t\\t}),\\n\\t\\thttputil.WithMaxHeaderBytes(HTTPMaxHeaderSize))\\n\\tif err != nil {\\n\\t\\treturn nil, errors.Join(fmt.Errorf(\\\"failed to start HTTP server: %w\\\", err), hs.Stop(ctx))\\n\\t}\\n\\ths.http = srv\\n\\n\\treturn hs, nil\\n}\\n\\nfunc Handler(l log.Logger, metrics Metrics) http.HandlerFunc {\\n\\treturn func(w http.ResponseWriter, r *http.Request) {\\n\\t\\tipStr := r.Header.Get(\\\"X-Forwarded-For\\\")\\n\\t\\t// XFF can be a comma-separated list. Left-most is the original client.\\n\\t\\tif i := strings.Index(ipStr, \\\",\\\"); i \\u003e= 0 {\\n\\t\\t\\tipStr = ipStr[:i]\\n\\t\\t}\\n\\n\\t\\tinnerL := l.New(\\n\\t\\t\\t\\\"ip\\\", ipStr,\\n\\t\\t\\t\\\"user_agent\\\", r.Header.Get(\\\"User-Agent\\\"),\\n\\t\\t\\t\\\"remote_addr\\\", r.RemoteAddr,\\n\\t\\t)\\n\\n\\t\\tvar payload heartbeat.Payload\\n\\t\\tdec := json.NewDecoder(io.LimitReader(r.Body, int64(HTTPMaxBodySize)))\\n\\t\\tif err := dec.Decode(\\u0026payload); err != nil {\\n\\t\\t\\tinnerL.Info(\\\"error decoding request payload\\\", \\\"err\\\", err)\\n\\t\\t\\tw.WriteHeader(400)\\n\\t\\t\\treturn\\n\\t\\t}\\n\\n\\t\\tinnerL.Info(\\n\\t\\t\\t\\\"got heartbeat\\\",\\n\\t\\t\\t\\\"version\\\", payload.Version,\\n\\t\\t\\t\\\"meta\\\", payload.Meta,\\n\\t\\t\\t\\\"moniker\\\", payload.Moniker,\\n\\t\\t\\t\\\"peer_id\\\", payload.PeerID,\\n\\t\\t\\t\\\"chain_id\\\", payload.ChainID,\\n\\t\\t)\\n\\n\\t\\tmetrics.RecordHeartbeat(payload, ipStr)\\n\\n\\t\\tw.WriteHeader(204)\\n\\t}\\n}\\n\\nfunc HealthzHandler(w http.ResponseWriter, r *http.Request) {\\n\\tw.WriteHeader(204)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(version string) func(ctx *cli.Context) error {\",\"lineNumber\":33,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/bnb-chain/opbnb\"},\"file\":{\"path\":\"op-program/host/host.go\",\"url\":\"/github.com/bnb-chain/opbnb/-/blob/op-program/host/host.go\",\"content\":\"package host\\n\\nimport (\\n\\t\\\"context\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"io\\\"\\n\\t\\\"io/fs\\\"\\n\\t\\\"os\\\"\\n\\t\\\"os/exec\\\"\\n\\n\\t\\\"github.com/ethereum-optimism/optimism/op-node/chaincfg\\\"\\n\\tpreimage \\\"github.com/ethereum-optimism/optimism/op-preimage\\\"\\n\\tcl \\\"github.com/ethereum-optimism/optimism/op-program/client\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-program/host/config\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-program/host/flags\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-program/host/kvstore\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-program/host/prefetcher\\\"\\n\\toppio \\\"github.com/ethereum-optimism/optimism/op-program/io\\\"\\n\\topservice \\\"github.com/ethereum-optimism/optimism/op-service\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-service/client\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-service/sources\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/common\\\"\\n\\t\\\"github.com/ethereum/go-ethereum/log\\\"\\n)\\n\\ntype L2Source struct {\\n\\t*L2Client\\n\\t*sources.DebugClient\\n}\\n\\nfunc Main(logger log.Logger, cfg *config.Config) error {\\n\\tif err := cfg.Check(); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"invalid config: %w\\\", err)\\n\\t}\\n\\topservice.ValidateEnvVars(flags.EnvVarPrefix, flags.Flags, logger)\\n\\tcfg.Rollup.LogDescription(logger, chaincfg.L2ChainIDToNetworkDisplayName)\\n\\n\\tctx := context.Background()\\n\\tif cfg.ServerMode {\\n\\t\\tpreimageChan := cl.CreatePreimageChannel()\\n\\t\\thinterChan := cl.CreateHinterChannel()\\n\\t\\treturn PreimageServer(ctx, logger, cfg, preimageChan, hinterChan)\\n\\t}\\n\\n\\tif err := FaultProofProgram(ctx, logger, cfg); err != nil {\\n\\t\\treturn err\\n\\t}\\n\\tlog.Info(\\\"Claim successfully verified\\\")\\n\\treturn nil\\n}\\n\\n// FaultProofProgram is the programmatic entry-point for the fault proof program\\nfunc FaultProofProgram(ctx context.Context, logger log.Logger, cfg *config.Config) error {\\n\\tvar (\\n\\t\\tserverErr chan error\\n\\t\\tpClientRW oppio.FileChannel\\n\\t\\thClientRW oppio.FileChannel\\n\\t)\\n\\tdefer func() {\\n\\t\\tif pClientRW != nil {\\n\\t\\t\\t_ = pClientRW.Close()\\n\\t\\t}\\n\\t\\tif hClientRW != nil {\\n\\t\\t\\t_ = hClientRW.Close()\\n\\t\\t}\\n\\t\\tif serverErr != nil {\\n\\t\\t\\terr := \\u003c-serverErr\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\tlogger.Error(\\\"preimage server failed\\\", \\\"err\\\", err)\\n\\t\\t\\t}\\n\\t\\t\\tlogger.Debug(\\\"Preimage server stopped\\\")\\n\\t\\t}\\n\\t}()\\n\\t// Setup client I/O for preimage oracle interaction\\n\\tpClientRW, pHostRW, err := oppio.CreateBidirectionalChannel()\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to create preimage pipe: %w\\\", err)\\n\\t}\\n\\n\\t// Setup client I/O for hint comms\\n\\thClientRW, hHostRW, err := oppio.CreateBidirectionalChannel()\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to create hints pipe: %w\\\", err)\\n\\t}\\n\\n\\t// Use a channel to receive the server result so we can wait for it to complete before returning\\n\\tserverErr = make(chan error)\\n\\tgo func() {\\n\\t\\tdefer close(serverErr)\\n\\t\\tserverErr \\u003c- PreimageServer(ctx, logger, cfg, pHostRW, hHostRW)\\n\\t}()\\n\\n\\tvar cmd *exec.Cmd\\n\\tif cfg.ExecCmd != \\\"\\\" {\\n\\t\\tcmd = exec.CommandContext(ctx, cfg.ExecCmd)\\n\\t\\tcmd.ExtraFiles = make([]*os.File, cl.MaxFd-3) // not including stdin, stdout and stderr\\n\\t\\tcmd.ExtraFiles[cl.HClientRFd-3] = hClientRW.Reader()\\n\\t\\tcmd.ExtraFiles[cl.HClientWFd-3] = hClientRW.Writer()\\n\\t\\tcmd.ExtraFiles[cl.PClientRFd-3] = pClientRW.Reader()\\n\\t\\tcmd.ExtraFiles[cl.PClientWFd-3] = pClientRW.Writer()\\n\\t\\tcmd.Stdout = os.Stdout // for debugging\\n\\t\\tcmd.Stderr = os.Stderr // for debugging\\n\\n\\t\\terr := cmd.Start()\\n\\t\\tif err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"program cmd failed to start: %w\\\", err)\\n\\t\\t}\\n\\t\\tif err := cmd.Wait(); err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to wait for child program: %w\\\", err)\\n\\t\\t}\\n\\t\\tlogger.Debug(\\\"Client program completed successfully\\\")\\n\\t\\treturn nil\\n\\t} else {\\n\\t\\treturn cl.RunProgram(logger, pClientRW, hClientRW)\\n\\t}\\n}\\n\\n// PreimageServer reads hints and preimage requests from the provided channels and processes those requests.\\n// This method will block until both the hinter and preimage handlers complete.\\n// If either returns an error both handlers are stopped.\\n// The supplied preimageChannel and hintChannel will be closed before this function returns.\\nfunc PreimageServer(ctx context.Context, logger log.Logger, cfg *config.Config, preimageChannel oppio.FileChannel, hintChannel oppio.FileChannel) error {\\n\\tvar serverDone chan error\\n\\tvar hinterDone chan error\\n\\tdefer func() {\\n\\t\\tpreimageChannel.Close()\\n\\t\\thintChannel.Close()\\n\\t\\tif serverDone != nil {\\n\\t\\t\\t// Wait for pre-image server to complete\\n\\t\\t\\t\\u003c-serverDone\\n\\t\\t}\\n\\t\\tif hinterDone != nil {\\n\\t\\t\\t// Wait for hinter to complete\\n\\t\\t\\t\\u003c-hinterDone\\n\\t\\t}\\n\\t}()\\n\\tlogger.Info(\\\"Starting preimage server\\\")\\n\\tvar kv kvstore.KV\\n\\tif cfg.DataDir == \\\"\\\" {\\n\\t\\tlogger.Info(\\\"Using in-memory storage\\\")\\n\\t\\tkv = kvstore.NewMemKV()\\n\\t} else {\\n\\t\\tlogger.Info(\\\"Creating disk storage\\\", \\\"datadir\\\", cfg.DataDir)\\n\\t\\tif err := os.MkdirAll(cfg.DataDir, 0755); err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"creating datadir: %w\\\", err)\\n\\t\\t}\\n\\t\\tkv = kvstore.NewDiskKV(cfg.DataDir)\\n\\t}\\n\\n\\tvar (\\n\\t\\tgetPreimage kvstore.PreimageSource\\n\\t\\thinter      preimage.HintHandler\\n\\t)\\n\\tif cfg.FetchingEnabled() {\\n\\t\\tprefetch, err := makePrefetcher(ctx, logger, kv, cfg)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to create prefetcher: %w\\\", err)\\n\\t\\t}\\n\\t\\tgetPreimage = func(key common.Hash) ([]byte, error) { return prefetch.GetPreimage(ctx, key) }\\n\\t\\thinter = prefetch.Hint\\n\\t} else {\\n\\t\\tlogger.Info(\\\"Using offline mode. All required pre-images must be pre-populated.\\\")\\n\\t\\tgetPreimage = kv.Get\\n\\t\\thinter = func(hint string) error {\\n\\t\\t\\tlogger.Debug(\\\"ignoring prefetch hint\\\", \\\"hint\\\", hint)\\n\\t\\t\\treturn nil\\n\\t\\t}\\n\\t}\\n\\n\\tlocalPreimageSource := kvstore.NewLocalPreimageSource(cfg)\\n\\tsplitter := kvstore.NewPreimageSourceSplitter(localPreimageSource.Get, getPreimage)\\n\\tpreimageGetter := preimage.WithVerification(splitter.Get)\\n\\n\\tserverDone = launchOracleServer(logger, preimageChannel, preimageGetter)\\n\\thinterDone = routeHints(logger, hintChannel, hinter)\\n\\tselect {\\n\\tcase err := \\u003c-serverDone:\\n\\t\\treturn err\\n\\tcase err := \\u003c-hinterDone:\\n\\t\\treturn err\\n\\t}\\n}\\n\\nfunc makePrefetcher(ctx context.Context, logger log.Logger, kv kvstore.KV, cfg *config.Config) (*prefetcher.Prefetcher, error) {\\n\\tlogger.Info(\\\"Connecting to L1 node\\\", \\\"l1\\\", cfg.L1URL)\\n\\tl1RPC, err := client.NewRPC(ctx, logger, cfg.L1URL, client.WithDialBackoff(10))\\n\\tif err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"failed to setup L1 RPC: %w\\\", err)\\n\\t}\\n\\n\\tlogger.Info(\\\"Connecting to L2 node\\\", \\\"l2\\\", cfg.L2URL)\\n\\tl2RPC, err := client.NewRPC(ctx, logger, cfg.L2URL, client.WithDialBackoff(10))\\n\\tif err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"failed to setup L2 RPC: %w\\\", err)\\n\\t}\\n\\n\\tl1ClCfg := sources.L1ClientDefaultConfig(cfg.Rollup, cfg.L1TrustRPC, cfg.L1RPCKind)\\n\\tl2ClCfg := sources.L2ClientDefaultConfig(cfg.Rollup, true)\\n\\tl1Cl, err := sources.NewL1Client(l1RPC, logger, nil, l1ClCfg)\\n\\tif err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"failed to create L1 client: %w\\\", err)\\n\\t}\\n\\tl1Beacon := sources.NewBeaconHTTPClient(client.NewBasicHTTPClient(cfg.L1BeaconURL, logger))\\n\\tl1BlobFetcher := sources.NewL1BeaconClient(l1Beacon, sources.L1BeaconClientConfig{FetchAllSidecars: false})\\n\\tl2Cl, err := NewL2Client(l2RPC, logger, nil, \\u0026L2ClientConfig{L2ClientConfig: l2ClCfg, L2Head: cfg.L2Head})\\n\\tif err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"failed to create L2 client: %w\\\", err)\\n\\t}\\n\\tl2DebugCl := \\u0026L2Source{L2Client: l2Cl, DebugClient: sources.NewDebugClient(l2RPC.CallContext)}\\n\\treturn prefetcher.NewPrefetcher(logger, l1Cl, l1BlobFetcher, l2DebugCl, kv), nil\\n}\\n\\nfunc routeHints(logger log.Logger, hHostRW io.ReadWriter, hinter preimage.HintHandler) chan error {\\n\\tchErr := make(chan error)\\n\\thintReader := preimage.NewHintReader(hHostRW)\\n\\tgo func() {\\n\\t\\tdefer close(chErr)\\n\\t\\tfor {\\n\\t\\t\\tif err := hintReader.NextHint(hinter); err != nil {\\n\\t\\t\\t\\tif err == io.EOF || errors.Is(err, fs.ErrClosed) {\\n\\t\\t\\t\\t\\tlogger.Debug(\\\"closing pre-image hint handler\\\")\\n\\t\\t\\t\\t\\treturn\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tlogger.Error(\\\"pre-image hint router error\\\", \\\"err\\\", err)\\n\\t\\t\\t\\tchErr \\u003c- err\\n\\t\\t\\t\\treturn\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}()\\n\\treturn chErr\\n}\\n\\nfunc launchOracleServer(logger log.Logger, pHostRW io.ReadWriteCloser, getter preimage.PreimageGetter) chan error {\\n\\tchErr := make(chan error)\\n\\tserver := preimage.NewOracleServer(pHostRW)\\n\\tgo func() {\\n\\t\\tdefer close(chErr)\\n\\t\\tfor {\\n\\t\\t\\tif err := server.NextPreimageRequest(getter); err != nil {\\n\\t\\t\\t\\tif err == io.EOF || errors.Is(err, fs.ErrClosed) {\\n\\t\\t\\t\\t\\tlogger.Debug(\\\"closing pre-image server\\\")\\n\\t\\t\\t\\t\\treturn\\n\\t\\t\\t\\t}\\n\\t\\t\\t\\tlogger.Error(\\\"pre-image server error\\\", \\\"error\\\", err)\\n\\t\\t\\t\\tchErr \\u003c- err\\n\\t\\t\\t\\treturn\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}()\\n\\treturn chErr\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(logger log.Logger, cfg *config.Config) error {\",\"lineNumber\":31,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/google/trillian-examples\"},\"file\":{\"path\":\"binary_transparency/firmware/cmd/flash_tool/impl/flash_tool.go\",\"url\":\"/github.com/google/trillian-examples/-/blob/binary_transparency/firmware/cmd/flash_tool/impl/flash_tool.go\",\"content\":\"// Copyright 2020 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//     http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\n// Package impl is the implementation of a util to flash firmware update packages created by the publisher tool onto devices.\\n//\\n// Currently, the only device is a dummy device, which simply sorts the firmware+metadata on local disk.\\npackage impl\\n\\nimport (\\n\\t\\\"bytes\\\"\\n\\t\\\"context\\\"\\n\\t\\\"crypto/sha512\\\"\\n\\t\\\"encoding/json\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"net/url\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"github.com/golang/glog\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/api\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/cmd/flash_tool/devices\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/devices/dummy\\\"\\n\\tarmory_flash \\\"github.com/google/trillian-examples/binary_transparency/firmware/devices/usbarmory/flash\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/internal/client\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/internal/verify\\\"\\n\\t\\\"github.com/google/trillian/merkle/coniks\\\"\\n\\t\\\"github.com/google/trillian/merkle/smt/node\\\"\\n\\t\\\"golang.org/x/mod/sumdb/note\\\"\\n)\\n\\n// FlashOpts encapsulates flash tool parameters.\\ntype FlashOpts struct {\\n\\tDeviceID       string\\n\\tLogURL         string\\n\\tLogSigVerifier note.Verifier\\n\\tMapURL         string\\n\\tWitnessURL     string\\n\\tUpdateFile     string\\n\\tForce          bool\\n\\tDeviceStorage  string\\n}\\n\\n// Main flashes the device according to the options provided.\\nfunc Main(ctx context.Context, opts FlashOpts) error {\\n\\tlogURL, err := url.Parse(opts.LogURL)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"log_url is invalid: %w\\\", err)\\n\\t}\\n\\tc := \\u0026client.ReadonlyClient{LogURL: logURL}\\n\\n\\tup, err := readUpdateFile(opts.UpdateFile)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to read update package file: %w\\\", err)\\n\\t}\\n\\n\\tdev, err := getDevice(opts)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to get device: %w\\\", err)\\n\\t}\\n\\n\\tpb, fwMeta, err := verifyUpdate(c, opts.LogSigVerifier, up, dev)\\n\\tif err != nil {\\n\\t\\terr := fmt.Errorf(\\\"failed to validate update: %w\\\", err)\\n\\t\\tif !opts.Force {\\n\\t\\t\\treturn err\\n\\t\\t}\\n\\t\\tglog.Warning(err)\\n\\t}\\n\\n\\tif len(opts.WitnessURL) \\u003e 0 {\\n\\t\\terr := verifyWitness(c, opts.LogSigVerifier, pb, opts.WitnessURL)\\n\\t\\tif err != nil {\\n\\t\\t\\tif !opts.Force {\\n\\t\\t\\t\\treturn err\\n\\t\\t\\t}\\n\\t\\t\\tglog.Warning(err)\\n\\t\\t}\\n\\t}\\n\\n\\tif len(opts.MapURL) \\u003e 0 {\\n\\t\\terr := verifyAnnotations(ctx, c, opts.LogSigVerifier, pb, fwMeta, opts.MapURL)\\n\\t\\tif err != nil {\\n\\t\\t\\tif !opts.Force {\\n\\t\\t\\t\\treturn fmt.Errorf(\\\"verifyAnnotations: %w\\\", err)\\n\\t\\t\\t}\\n\\t\\t\\tglog.Warning(err)\\n\\t\\t}\\n\\t}\\n\\tglog.Info(\\\"Update verified, about to apply to device...\\\")\\n\\n\\tif err := dev.ApplyUpdate(up); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to apply update to device: %w\\\", err)\\n\\t}\\n\\n\\tglog.Info(\\\"Update applied.\\\")\\n\\treturn nil\\n}\\n\\nfunc getDevice(opts FlashOpts) (devices.Device, error) {\\n\\tvar dev devices.Device\\n\\tvar err error\\n\\tswitch opts.DeviceID {\\n\\tcase \\\"armory\\\":\\n\\t\\tdev, err = armory_flash.New(opts.DeviceStorage)\\n\\tcase \\\"dummy\\\":\\n\\t\\tdev, err = dummy.New(opts.DeviceStorage)\\n\\tdefault:\\n\\t\\treturn dev, errors.New(\\\"device must be one of: 'dummy', 'armory'\\\")\\n\\t}\\n\\tif err != nil {\\n\\t\\tswitch t := err.(type) {\\n\\t\\tcase devices.ErrNeedsInit:\\n\\t\\t\\terr := fmt.Errorf(\\\"device needs to be force initialised: %w\\\", err)\\n\\t\\t\\tif !opts.Force {\\n\\t\\t\\t\\treturn dev, err\\n\\t\\t\\t}\\n\\t\\t\\tglog.Warning(err)\\n\\t\\tdefault:\\n\\t\\t\\terr := fmt.Errorf(\\\"failed to open device: %w\\\", t)\\n\\t\\t\\tif !opts.Force {\\n\\t\\t\\t\\treturn dev, err\\n\\t\\t\\t}\\n\\t\\t\\tglog.Warning(err)\\n\\t\\t}\\n\\t}\\n\\treturn dev, nil\\n}\\n\\nfunc readUpdateFile(path string) (api.UpdatePackage, error) {\\n\\tif len(path) == 0 {\\n\\t\\treturn api.UpdatePackage{}, errors.New(\\\"must specify update_file\\\")\\n\\t}\\n\\n\\tf, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)\\n\\tif err != nil {\\n\\t\\tglog.Exitf(\\\"Failed to open update package file %q: %q\\\", path, err)\\n\\t}\\n\\tdefer func() {\\n\\t\\tif err := f.Close(); err != nil {\\n\\t\\t\\tglog.Errorf(\\\"f.Close(): %v\\\", err)\\n\\t\\t}\\n\\t}()\\n\\n\\tvar up api.UpdatePackage\\n\\tif err := json.NewDecoder(f).Decode(\\u0026up); err != nil {\\n\\t\\tglog.Exitf(\\\"Failed to parse update package file: %q\\\", err)\\n\\t}\\n\\treturn up, nil\\n}\\n\\n// getConsistencyFunc executes on a given client context and returns a\\n// consistency function.\\nfunc getConsistencyFunc(c *client.ReadonlyClient) func(from, to uint64) ([][]byte, error) {\\n\\tcpFunc := func(from, to uint64) ([][]byte, error) {\\n\\t\\tvar cp [][]byte\\n\\t\\tif from \\u003e 0 {\\n\\t\\t\\tr, err := c.GetConsistencyProof(api.GetConsistencyRequest{From: from, To: to})\\n\\t\\t\\tif err != nil {\\n\\t\\t\\t\\treturn nil, fmt.Errorf(\\\"failed to fetch consistency proof: %w\\\", err)\\n\\t\\t\\t}\\n\\t\\t\\tcp = r.Proof\\n\\t\\t}\\n\\t\\treturn cp, nil\\n\\t}\\n\\treturn cpFunc\\n}\\n\\n// verifyUpdate checks that an update package is self-consistent and returns a verified proof bundle\\nfunc verifyUpdate(c *client.ReadonlyClient, logSigVerifier note.Verifier, up api.UpdatePackage, dev devices.Device) (api.ProofBundle, api.FirmwareMetadata, error) {\\n\\tvar pb api.ProofBundle\\n\\tvar fwMeta api.FirmwareMetadata\\n\\n\\t// Get the consistency proof for the bundle\\n\\tn, err := dev.DeviceCheckpoint()\\n\\tif err != nil {\\n\\t\\treturn pb, fwMeta, fmt.Errorf(\\\"failed to fetch the device checkpoint: %w\\\", err)\\n\\t}\\n\\tdc, err := api.ParseCheckpoint([]byte(n), logSigVerifier)\\n\\tif err != nil {\\n\\t\\treturn pb, fwMeta, fmt.Errorf(\\\"failed to open the device checkpoint: %w\\\", err)\\n\\t}\\n\\n\\tcpFunc := getConsistencyFunc(c)\\n\\tfwHash := sha512.Sum512(up.FirmwareImage)\\n\\tpb, fwMeta, err = verify.BundleForUpdate(up.ProofBundle, fwHash[:], *dc, cpFunc, logSigVerifier)\\n\\tif err != nil {\\n\\t\\treturn pb, fwMeta, fmt.Errorf(\\\"failed to verify proof bundle: %w\\\", err)\\n\\t}\\n\\treturn pb, fwMeta, nil\\n}\\n\\nfunc verifyWitness(c *client.ReadonlyClient, logSigVerifier note.Verifier, pb api.ProofBundle, witnessURL string) error {\\n\\twURL, err := url.Parse(witnessURL)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"witness_url is invalid: %w\\\", err)\\n\\t}\\n\\twc := client.WitnessClient{\\n\\t\\tURL:            wURL,\\n\\t\\tLogSigVerifier: logSigVerifier,\\n\\t}\\n\\n\\twcp, err := wc.GetWitnessCheckpoint()\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to fetch the witness checkpoint: %w\\\", err)\\n\\t}\\n\\tif wcp.Size == 0 {\\n\\t\\treturn fmt.Errorf(\\\"no witness checkpoint to verify\\\")\\n\\t}\\n\\tif err := verify.BundleConsistency(pb, *wcp, getConsistencyFunc(c), logSigVerifier); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to verify checkpoint consistency against witness: %w\\\", err)\\n\\t}\\n\\treturn nil\\n}\\n\\nfunc verifyAnnotations(ctx context.Context, c *client.ReadonlyClient, logSigVerifier note.Verifier, pb api.ProofBundle, fwMeta api.FirmwareMetadata, mapURL string) error {\\n\\tmc, err := client.NewMapClient(mapURL)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to create map client: %w\\\", err)\\n\\t}\\n\\tmcp, err := mc.MapCheckpoint()\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to get map checkpoint: %w\\\", err)\\n\\t}\\n\\t// The map checkpoint should be stored in a log, and this client should follow\\n\\t// the log, checking consistency proofs and maintaining a golden checkpoint.\\n\\t// Without this, the client is at risk of being given a custom map root that\\n\\t// nobody else in the world sees.\\n\\tglog.V(1).Infof(\\\"Received map checkpoint: %s\\\", mcp.LogCheckpoint)\\n\\tvar lcp api.LogCheckpoint\\n\\tif err := json.Unmarshal(mcp.LogCheckpoint, \\u0026lcp); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to unmarshal log checkpoint: %w\\\", err)\\n\\t}\\n\\t// TODO(mhutchinson): check consistency with the largest checkpoint found thus far\\n\\t// in order to detect a class of fork; it could be that the checkpoint in the update\\n\\t// is consistent with the map and the witness, but the map and the witness aren't\\n\\t// consistent with each other.\\n\\tif err := verify.BundleConsistency(pb, lcp, getConsistencyFunc(c), logSigVerifier); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to verify update with map checkpoint: %w\\\", err)\\n\\t}\\n\\n\\t// Get the aggregation and proof, and then check everything about it.\\n\\t// This is a little paranoid as the inclusion proof is generated client-side.\\n\\t// The pretense here is that there is a trust boundary between the code in this class,\\n\\t// and everything else. In a production system, it is likely that the proof generation\\n\\t// would live elsewhere (e.g. in the OTA packaging process), and the shorter proof\\n\\t// bundle would be provided to the device.\\n\\tpreimage, ip, err := mc.Aggregation(ctx, mcp.Revision, pb.InclusionProof.LeafIndex)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to get map value for %q: %w\\\", pb.InclusionProof.LeafIndex, err)\\n\\t}\\n\\t// 1. Check: the proof is for the correct key.\\n\\tkbs := sha512.Sum512_256([]byte(fmt.Sprintf(\\\"summary:%d\\\", pb.InclusionProof.LeafIndex)))\\n\\tif !bytes.Equal(ip.Key, kbs[:]) {\\n\\t\\treturn fmt.Errorf(\\\"received inclusion proof for key %x but wanted %x\\\", ip.Key, kbs[:])\\n\\t}\\n\\t// 2. Check: the proof is for the correct value.\\n\\tleafID := node.NewID(string(kbs[:]), 256)\\n\\thasher := coniks.Default\\n\\texpectedCommitment := hasher.HashLeaf(api.MapTreeID, leafID, preimage)\\n\\tif !bytes.Equal(ip.Value, expectedCommitment) {\\n\\t\\t// This could happen if the JSON roundtripping was not stable. If we see that happen,\\n\\t\\t// then we'll need to pass out the raw bytes received from the server and parse into\\n\\t\\t// the struct at a higher level.\\n\\t\\t// It could also happen because the value returned is not actually committed to by the map.\\n\\t\\treturn fmt.Errorf(\\\"received inclusion proof for value %x but wanted %x\\\", ip.Value, expectedCommitment)\\n\\t}\\n\\t// 3. Check: the inclusion proof evaluates to the map root that we've obtained.\\n\\t// The calculation starts from the leaf, and uses the siblings from the inclusion proof\\n\\t// to generate the root, which is then compared with the map checkpoint.\\n\\tcalc := expectedCommitment\\n\\tfor pd := hasher.BitLen(); pd \\u003e 0; pd-- {\\n\\t\\tsib := ip.Proof[pd-1]\\n\\t\\tstem := leafID.Prefix(uint(pd))\\n\\t\\tif sib == nil {\\n\\t\\t\\tsib = hasher.HashEmpty(api.MapTreeID, stem.Sibling())\\n\\t\\t}\\n\\t\\tleft, right := calc, sib\\n\\t\\tif !isLeftChild(stem) {\\n\\t\\t\\tleft, right = right, left\\n\\t\\t}\\n\\t\\tcalc = hasher.HashChildren(left, right)\\n\\t}\\n\\tif !bytes.Equal(calc, mcp.RootHash) {\\n\\t\\treturn fmt.Errorf(\\\"inclusion proof calculated root %x but wanted %x\\\", calc, mcp.RootHash)\\n\\t}\\n\\n\\tvar agg api.AggregatedFirmware\\n\\tif err := json.Unmarshal(preimage, \\u0026agg); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to decode aggregation: %w\\\", err)\\n\\t}\\n\\t// Now we're certain that the aggregation is contained in the map, we can use the value.\\n\\tif !agg.Good {\\n\\t\\treturn errors.New(\\\"firmware is marked as bad\\\")\\n\\t}\\n\\treturn nil\\n}\\n\\n// isLeftChild returns whether the given node is a left child.\\nfunc isLeftChild(id node.ID) bool {\\n\\tlast, bits := id.LastByte()\\n\\treturn last\\u0026(1\\u003c\\u003c(8-bits)) == 0\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(ctx context.Context, opts FlashOpts) error {\",\"lineNumber\":54,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/google/trillian-examples\"},\"file\":{\"path\":\"binary_transparency/firmware/cmd/ft_monitor/impl/ft_monitor.go\",\"url\":\"/github.com/google/trillian-examples/-/blob/binary_transparency/firmware/cmd/ft_monitor/impl/ft_monitor.go\",\"content\":\"// Copyright 2020 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//     http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\n// Package impl is the implementation of the Firmware Transparency monitor.\\n// The monitor follows the growth of the Firmware Transparency log server,\\n// inspects new firmware metadata as it appears, and prints out a short\\n// summary.\\n//\\n// TODO(al): Extend monitor to verify claims.\\npackage impl\\n\\nimport (\\n\\t\\\"bytes\\\"\\n\\t\\\"context\\\"\\n\\t\\\"crypto/sha512\\\"\\n\\t\\\"encoding/json\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"net/url\\\"\\n\\t\\\"os\\\"\\n\\t\\\"regexp\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/golang/glog\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/api\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/internal/client\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/internal/crypto\\\"\\n\\t\\\"golang.org/x/mod/sumdb/note\\\"\\n)\\n\\n// MatchFunc is the signature of a function which can be called by the monitor\\n// to signal when it's found a keyword match in a logged entry.\\ntype MatchFunc func(index uint64, fw api.FirmwareMetadata)\\n\\n// MonitorOpts encapsulates options for running the monitor.\\ntype MonitorOpts struct {\\n\\tLogURL         string\\n\\tLogSigVerifier note.Verifier\\n\\tPollInterval   time.Duration\\n\\tKeyword        string\\n\\tMatched        MatchFunc\\n\\tAnnotate       bool\\n\\tStateFile      string\\n}\\n\\n// Main runs the monitor until the context is canceled.\\nfunc Main(ctx context.Context, opts MonitorOpts) error {\\n\\tif len(opts.LogURL) == 0 {\\n\\t\\treturn errors.New(\\\"log URL is required\\\")\\n\\t}\\n\\tif len(opts.StateFile) == 0 {\\n\\t\\treturn errors.New(\\\"state file is required\\\")\\n\\t}\\n\\n\\tftURL, err := url.Parse(opts.LogURL)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to parse FT log URL: %w\\\", err)\\n\\t}\\n\\n\\t// Parse the input keywords as regular expression\\n\\tmatcher := regexp.MustCompile(opts.Keyword)\\n\\n\\tc := client.ReadonlyClient{\\n\\t\\tLogURL:         ftURL,\\n\\t\\tLogSigVerifier: opts.LogSigVerifier,\\n\\t}\\n\\n\\t// Initialize the checkpoint from persisted state.\\n\\tvar latestCP api.LogCheckpoint\\n\\tif state, err := os.ReadFile(opts.StateFile); err != nil {\\n\\t\\tif !errors.Is(err, os.ErrNotExist) {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to read state: %w\\\", err)\\n\\t\\t}\\n\\t\\t// This could fail here unless a force flag is provided, for better security.\\n\\t\\tglog.Warningf(\\\"State file %q did not exist; first log checkpoint will be trusted implicitly\\\", opts.StateFile)\\n\\t} else {\\n\\t\\tcp, err := api.ParseCheckpoint(state, opts.LogSigVerifier)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to open state: %w\\\", err)\\n\\t\\t}\\n\\t\\tlatestCP = *cp\\n\\t}\\n\\thead := latestCP.Size\\n\\tfollow := client.NewLogFollower(c)\\n\\n\\tglog.Infof(\\\"Monitoring FT log (%q) starting from index %d\\\", opts.LogURL, head)\\n\\tcpc, cperrc := follow.Checkpoints(ctx, opts.PollInterval, latestCP)\\n\\tec, eerrc := follow.Entries(ctx, cpc, head)\\n\\n\\tfor {\\n\\t\\tvar entry client.LogEntry\\n\\t\\tselect {\\n\\t\\tcase err = \\u003c-cperrc:\\n\\t\\t\\treturn err\\n\\t\\tcase err = \\u003c-eerrc:\\n\\t\\t\\treturn err\\n\\t\\tcase \\u003c-ctx.Done():\\n\\t\\t\\treturn ctx.Err()\\n\\t\\tcase entry = \\u003c-ec:\\n\\t\\t}\\n\\n\\t\\tif err := processEntry(entry, c, opts, matcher); err != nil {\\n\\t\\t\\t// TODO(mhutchinson): Consider a flag that causes processing errors to hard-fail.\\n\\t\\t\\tglog.Warningf(\\\"Warning processing entry at index %d: %q\\\", entry.Index, err)\\n\\t\\t}\\n\\n\\t\\tif entry.Index == entry.Root.Size-1 {\\n\\t\\t\\t// If we have processed all leaves in the current checkpoint, then persist this checkpoint\\n\\t\\t\\t// so that we don't repeat work on startup.\\n\\t\\t\\tif err := os.WriteFile(opts.StateFile, entry.Root.Envelope, 0o755); err != nil {\\n\\t\\t\\t\\tglog.Errorf(\\\"os.WriteFile(): %v\\\", err)\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}\\n}\\n\\nfunc processEntry(entry client.LogEntry, c client.ReadonlyClient, opts MonitorOpts, matcher *regexp.Regexp) error {\\n\\tstmt := entry.Value\\n\\tif stmt.Type != api.FirmwareMetadataType {\\n\\t\\t// Only analyze firmware statements in the monitor.\\n\\t\\treturn nil\\n\\t}\\n\\n\\t// Parse the firmware metadata:\\n\\tvar meta api.FirmwareMetadata\\n\\tif err := json.Unmarshal(stmt.Statement, \\u0026meta); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"unable to decode FW Metadata from Statement %q\\\", err)\\n\\t}\\n\\n\\tglog.Infof(\\\"Found firmware (@%d): %s\\\", entry.Index, meta)\\n\\n\\t// Fetch the Image from FT Personality\\n\\timage, err := c.GetFirmwareImage(meta.FirmwareImageSHA512)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"unable to GetFirmwareImage for Firmware with Hash 0x%x , reason %q\\\", meta.FirmwareImageSHA512, err)\\n\\t}\\n\\t// Verify Image Hash from log Manifest matches the actual image hash\\n\\th := sha512.Sum512(image)\\n\\tif !bytes.Equal(h[:], meta.FirmwareImageSHA512) {\\n\\t\\treturn fmt.Errorf(\\\"downloaded image does not match SHA512 in metadata (%x != %x)\\\", h[:], meta.FirmwareImageSHA512)\\n\\t}\\n\\tglog.V(1).Infof(\\\"Image Hash Verified for image at leaf index %d\\\", entry.Index)\\n\\n\\t// Search for specific keywords inside firmware image\\n\\tmalwareDetected := matcher.Match(image)\\n\\n\\tif malwareDetected {\\n\\t\\topts.Matched(entry.Index, meta)\\n\\t}\\n\\tif opts.Annotate {\\n\\t\\tms := api.MalwareStatement{\\n\\t\\t\\tFirmwareID: api.FirmwareID{\\n\\t\\t\\t\\tLogIndex:            entry.Index,\\n\\t\\t\\t\\tFirmwareImageSHA512: h[:],\\n\\t\\t\\t},\\n\\t\\t\\tGood: !malwareDetected,\\n\\t\\t}\\n\\t\\tglog.V(1).Infof(\\\"Annotating %s\\\", ms)\\n\\t\\tjs, err := createStatementJSON(ms)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to create annotation: %q\\\", err)\\n\\t\\t}\\n\\t\\tsc := client.SubmitClient{\\n\\t\\t\\tReadonlyClient: \\u0026c,\\n\\t\\t}\\n\\t\\tif err := sc.PublishAnnotationMalware(js); err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to publish annotation: %q\\\", err)\\n\\t\\t}\\n\\t}\\n\\treturn nil\\n}\\n\\nfunc createStatementJSON(m api.MalwareStatement) ([]byte, error) {\\n\\tjs, err := json.Marshal(m)\\n\\tif err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"failed to marshal metadata: %w\\\", err)\\n\\t}\\n\\tsig, err := crypto.AnnotatorMalware.SignMessage(api.MalwareStatementType, js)\\n\\tif err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"failed to generate signature: %w\\\", err)\\n\\t}\\n\\n\\tstatement := api.SignedStatement{\\n\\t\\tType:      api.MalwareStatementType,\\n\\t\\tStatement: js,\\n\\t\\tSignature: sig,\\n\\t}\\n\\n\\treturn json.Marshal(statement)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(ctx context.Context, opts MonitorOpts) error {\",\"lineNumber\":57,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/google/trillian-examples\"},\"file\":{\"path\":\"binary_transparency/firmware/cmd/ft_witness/impl/ft_witness.go\",\"url\":\"/github.com/google/trillian-examples/-/blob/binary_transparency/firmware/cmd/ft_witness/impl/ft_witness.go\",\"content\":\"// Copyright 2021 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//     http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\n// Package impl is the implementation of the Firmware Transparency witness server.\\n// This requires a FT Log to be running at a known address.\\npackage impl\\n\\nimport (\\n\\t\\\"context\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"net/http\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/golang/glog\\\"\\n\\tih \\\"github.com/google/trillian-examples/binary_transparency/firmware/cmd/ft_witness/internal/http\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/cmd/ft_witness/internal/ws\\\"\\n\\t\\\"github.com/gorilla/mux\\\"\\n\\t\\\"golang.org/x/mod/sumdb/note\\\"\\n)\\n\\n// WitnessOpts encapsulates options for running an FT witness.\\ntype WitnessOpts struct {\\n\\tListenAddr       string\\n\\tWSFile           string\\n\\tFtLogURL         string\\n\\tFtLogSigVerifier note.Verifier\\n\\tPollInterval     time.Duration\\n}\\n\\n// Main kickstarts the witness\\nfunc Main(ctx context.Context, opts WitnessOpts) error {\\n\\tif len(opts.WSFile) == 0 {\\n\\t\\treturn errors.New(\\\"witness store file is required\\\")\\n\\t}\\n\\n\\tws, err := ws.NewStorage(opts.WSFile)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to connect witness store: %w\\\", err)\\n\\t}\\n\\n\\tglog.Infof(\\\"Starting FT witness server...\\\")\\n\\twitness, err := ih.NewWitness(ws, opts.FtLogURL, opts.FtLogSigVerifier, opts.PollInterval)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to create new witness: %w\\\", err)\\n\\t}\\n\\tr := mux.NewRouter()\\n\\twitness.RegisterHandlers(r)\\n\\n\\tgo func() {\\n\\t\\tif err := witness.Poll(ctx); err != nil {\\n\\t\\t\\tglog.Errorf(\\\"witness.Poll(): %v\\\", err)\\n\\t\\t}\\n\\t}()\\n\\n\\thServer := \\u0026http.Server{\\n\\t\\tAddr:    opts.ListenAddr,\\n\\t\\tHandler: r,\\n\\t}\\n\\te := make(chan error, 1)\\n\\tgo func() {\\n\\t\\te \\u003c- hServer.ListenAndServe()\\n\\t\\tclose(e)\\n\\t}()\\n\\t\\u003c-ctx.Done()\\n\\tglog.Info(\\\"Server shutting down\\\")\\n\\tif err := hServer.Shutdown(ctx); err != nil {\\n\\t\\tglog.Errorf(\\\"server.Shutdown(): %v\\\", err)\\n\\t}\\n\\treturn \\u003c-e\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(ctx context.Context, opts WitnessOpts) error {\",\"lineNumber\":42,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/google/trillian-examples\"},\"file\":{\"path\":\"binary_transparency/firmware/cmd/ftmapserver/impl/ftmapserver.go\",\"url\":\"/github.com/google/trillian-examples/-/blob/binary_transparency/firmware/cmd/ftmapserver/impl/ftmapserver.go\",\"content\":\"// Copyright 2021 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//     http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\n// Package impl is the implementation of the Firmware Transparency map server.\\npackage impl\\n\\nimport (\\n\\t\\\"context\\\"\\n\\t\\\"encoding/base64\\\"\\n\\t\\\"encoding/json\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"math\\\"\\n\\t\\\"net/http\\\"\\n\\t\\\"strconv\\\"\\n\\n\\t\\\"github.com/golang/glog\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/api\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/internal/ftmap\\\"\\n\\t\\\"github.com/google/trillian/experimental/batchmap\\\"\\n\\t\\\"github.com/google/trillian/types\\\"\\n\\t\\\"github.com/transparency-dev/formats/log\\\"\\n\\n\\t\\\"github.com/gorilla/mux\\\"\\n\\n\\t_ \\\"github.com/mattn/go-sqlite3\\\" // Load drivers for sqlite3\\n)\\n\\n// MapReader is an interface that allows a map to be read from storage.\\ntype MapReader interface {\\n\\t// LatestRevision gets the metadata for the last completed write.\\n\\tLatestRevision() (rev int, logroot types.LogRootV1, count int64, err error)\\n\\n\\t// Tile gets the tile at the given path in the given revision of the map.\\n\\tTile(revision int, path []byte) (*batchmap.Tile, error)\\n\\n\\t// Aggregation gets the aggregation for the firmware at the given log index.\\n\\tAggregation(revision int, fwLogIndex uint64) (api.AggregatedFirmware, error)\\n}\\n\\n// MapServerOpts encapsulates options for running an FT map server.\\ntype MapServerOpts struct {\\n\\tListenAddr string\\n\\tMapDBAddr  string\\n}\\n\\n// Main brings up an http server according to the given options.\\nfunc Main(ctx context.Context, opts MapServerOpts) error {\\n\\tif len(opts.MapDBAddr) == 0 {\\n\\t\\treturn errors.New(\\\"map DB is required\\\")\\n\\t}\\n\\tmapDB, err := ftmap.NewMapDB(opts.MapDBAddr)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to open map DB at %q: %v\\\", opts.MapDBAddr, err)\\n\\t}\\n\\n\\tglog.Infof(\\\"Starting FT map server...\\\")\\n\\tsrv := Server{db: mapDB}\\n\\tr := mux.NewRouter()\\n\\tsrv.RegisterHandlers(r)\\n\\thServer := \\u0026http.Server{\\n\\t\\tAddr:    opts.ListenAddr,\\n\\t\\tHandler: r,\\n\\t}\\n\\te := make(chan error, 1)\\n\\tgo func() {\\n\\t\\te \\u003c- hServer.ListenAndServe()\\n\\t\\tclose(e)\\n\\t}()\\n\\t\\u003c-ctx.Done()\\n\\tglog.Info(\\\"Server shutting down\\\")\\n\\tif err := hServer.Shutdown(ctx); err != nil {\\n\\t\\tglog.Errorf(\\\"server.Shutdown(): %v\\\", err)\\n\\t}\\n\\treturn \\u003c-e\\n}\\n\\n// Server is the core state \\u0026 handler implementation of the FT personality.\\ntype Server struct {\\n\\tdb MapReader\\n}\\n\\n// getCheckpoint returns a recent MapCheckpoint.\\nfunc (s *Server) getCheckpoint(w http.ResponseWriter, r *http.Request) {\\n\\trev, logRootV1, count, err := s.db.LatestRevision()\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusInternalServerError)\\n\\t\\treturn\\n\\t}\\n\\tglog.V(1).Infof(\\\"Latest revision: %d %+v\\\", rev, logRootV1)\\n\\ttile, err := s.db.Tile(rev, []byte{})\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusInternalServerError)\\n\\t\\treturn\\n\\t}\\n\\n\\tlcp := api.LogCheckpoint{\\n\\t\\tCheckpoint: log.Checkpoint{\\n\\t\\t\\tOrigin: api.FTLogOrigin,\\n\\t\\t\\tSize:   logRootV1.TreeSize,\\n\\t\\t\\tHash:   logRootV1.RootHash,\\n\\t\\t},\\n\\t\\tTimestampNanos: logRootV1.TimestampNanos,\\n\\t}\\n\\tcheckpoint := api.MapCheckpoint{\\n\\t\\tLogCheckpoint: lcp.Marshal(),\\n\\t\\tLogSize:       uint64(count),\\n\\t\\tRevision:      uint64(rev),\\n\\t\\tRootHash:      tile.RootHash,\\n\\t}\\n\\tjs, err := json.Marshal(checkpoint)\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusInternalServerError)\\n\\n\\t}\\n\\tw.Header().Set(\\\"Content-Type\\\", \\\"application/json\\\")\\n\\tif _, err := w.Write(js); err != nil {\\n\\t\\tglog.Errorf(\\\"w.Write(): %v\\\", err)\\n\\t}\\n}\\n\\n// getTile returns the tile at the given revision \\u0026 path.\\nfunc (s *Server) getTile(w http.ResponseWriter, r *http.Request) {\\n\\trev, err := parseUintParam(r, \\\"revision\\\")\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusBadRequest)\\n\\t\\treturn\\n\\t}\\n\\tif rev \\u003e math.MaxInt {\\n\\t\\t// TODO(mhutchinson): Revision probably ought to be uint64 as negative revisions are weird.\\n\\t\\thttp.Error(w, \\\"revision is too large\\\", http.StatusBadRequest)\\n\\t\\treturn\\n\\t}\\n\\tpath, err := parseBase64Param(r, \\\"path\\\")\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusBadRequest)\\n\\t\\treturn\\n\\t}\\n\\tbmt, err := s.db.Tile(int(rev), path)\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusInternalServerError)\\n\\t\\treturn\\n\\t}\\n\\tleaves := make([]api.MapTileLeaf, len(bmt.Leaves))\\n\\tfor i, l := range bmt.Leaves {\\n\\t\\tleaves[i] = api.MapTileLeaf{\\n\\t\\t\\tPath: l.Path,\\n\\t\\t\\tHash: l.Hash,\\n\\t\\t}\\n\\t}\\n\\ttile := api.MapTile{\\n\\t\\tPath:   bmt.Path,\\n\\t\\tLeaves: leaves,\\n\\t}\\n\\tjs, err := json.Marshal(tile)\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusInternalServerError)\\n\\t\\treturn\\n\\t}\\n\\tw.Header().Set(\\\"Content-Type\\\", \\\"application/json\\\")\\n\\tif _, err := w.Write(js); err != nil {\\n\\t\\tglog.Errorf(\\\"w.Write(): %v\\\", err)\\n\\t}\\n}\\n\\n// getAggregation returns the aggregation for the firware at the given log index.\\nfunc (s *Server) getAggregation(w http.ResponseWriter, r *http.Request) {\\n\\trev, err := parseUintParam(r, \\\"revision\\\")\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusBadRequest)\\n\\t\\treturn\\n\\t}\\n\\tif rev \\u003e math.MaxInt {\\n\\t\\t// TODO(mhutchinson): Revision probably ought to be uint64 as negative revisions are weird.\\n\\t\\thttp.Error(w, \\\"revision is too large\\\", http.StatusBadRequest)\\n\\t\\treturn\\n\\t}\\n\\tfwIndex, err := parseUintParam(r, \\\"fwIndex\\\")\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusBadRequest)\\n\\t\\treturn\\n\\t}\\n\\n\\tagg, err := s.db.Aggregation(int(rev), fwIndex)\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusInternalServerError)\\n\\t\\treturn\\n\\t}\\n\\tjs, err := json.Marshal(agg)\\n\\tif err != nil {\\n\\t\\thttp.Error(w, err.Error(), http.StatusInternalServerError)\\n\\t\\treturn\\n\\t}\\n\\tw.Header().Set(\\\"Content-Type\\\", \\\"application/json\\\")\\n\\tif _, err := w.Write(js); err != nil {\\n\\t\\tglog.Errorf(\\\"w.Write(): %v\\\", err)\\n\\t}\\n}\\n\\n// RegisterHandlers registers HTTP handlers for the endpoints.\\nfunc (s *Server) RegisterHandlers(r *mux.Router) {\\n\\tr.HandleFunc(fmt.Sprintf(\\\"/%s\\\", api.MapHTTPGetCheckpoint), s.getCheckpoint).Methods(\\\"GET\\\")\\n\\t// Empty tile path is normal for requesting the root tile\\n\\tr.HandleFunc(fmt.Sprintf(\\\"/%s/in-revision/{revision:[0-9]+}/at-path/\\\", api.MapHTTPGetTile), s.getTile).Methods(\\\"GET\\\")\\n\\tr.HandleFunc(fmt.Sprintf(\\\"/%s/in-revision/{revision:[0-9]+}/at-path/{path}\\\", api.MapHTTPGetTile), s.getTile).Methods(\\\"GET\\\")\\n\\tr.HandleFunc(fmt.Sprintf(\\\"/%s/in-revision/{revision:[0-9]+}/for-firmware-at-index/{fwIndex:[0-9]+}\\\", api.MapHTTPGetAggregation), s.getAggregation).Methods(\\\"GET\\\")\\n}\\n\\nfunc parseBase64Param(r *http.Request, name string) ([]byte, error) {\\n\\tv := mux.Vars(r)\\n\\tb, err := base64.URLEncoding.DecodeString(v[name])\\n\\tif err != nil {\\n\\t\\treturn nil, fmt.Errorf(\\\"%s should be URL-safe base64 (%q)\\\", name, err)\\n\\t}\\n\\treturn b, nil\\n}\\n\\nfunc parseUintParam(r *http.Request, name string) (uint64, error) {\\n\\tv := mux.Vars(r)\\n\\ti, err := strconv.ParseUint(v[name], 0, 64)\\n\\tif err != nil {\\n\\t\\treturn 0, fmt.Errorf(\\\"%s should be an integer (%q)\\\", name, err)\\n\\t}\\n\\treturn i, nil\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(ctx context.Context, opts MapServerOpts) error {\",\"lineNumber\":58,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/google/trillian-examples\"},\"file\":{\"path\":\"binary_transparency/firmware/cmd/emulator/dummy/impl/inner_main.go\",\"url\":\"/github.com/google/trillian-examples/-/blob/binary_transparency/firmware/cmd/emulator/dummy/impl/inner_main.go\",\"content\":\"// Copyright 2020 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//     http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\n// Package impl is the implementation of the emulator for the dummy device.\\npackage impl\\n\\nimport (\\n\\t\\\"fmt\\\"\\n\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/devices/dummy/rom\\\"\\n)\\n\\n// EmulatorOpts encapsulates the parameters for running the emulator.\\ntype EmulatorOpts struct {\\n\\tDeviceStorage string\\n}\\n\\n// Main is the entry point for the dummy emulator\\nfunc Main(opts EmulatorOpts) error {\\n\\tboot, err := rom.Reset(opts.DeviceStorage)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"ROM: %w\\\", err)\\n\\t}\\n\\n\\tif err := boot(); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"boot(): %w\\\", err)\\n\\t}\\n\\n\\treturn nil\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(opts EmulatorOpts) error {\",\"lineNumber\":29,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/google/trillian-examples\"},\"file\":{\"path\":\"binary_transparency/firmware/cmd/ft_personality/impl/ft_personality.go\",\"url\":\"/github.com/google/trillian-examples/-/blob/binary_transparency/firmware/cmd/ft_personality/impl/ft_personality.go\",\"content\":\"// Copyright 2020 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//     http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\n// Package impl is the implementation of the Firmware Transparency personality server.\\n// This requires a Trillian instance to be reachable via gRPC and a tree to have\\n// been provisioned.\\npackage impl\\n\\nimport (\\n\\t\\\"context\\\"\\n\\t\\\"database/sql\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"net/http\\\"\\n\\t\\\"time\\\"\\n\\n\\t\\\"github.com/golang/glog\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/cmd/ft_personality/internal/cas\\\"\\n\\tih \\\"github.com/google/trillian-examples/binary_transparency/firmware/cmd/ft_personality/internal/http\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/cmd/ft_personality/internal/trees\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/cmd/ft_personality/internal/trillian\\\"\\n\\t\\\"github.com/gorilla/mux\\\"\\n\\t\\\"golang.org/x/mod/sumdb/note\\\"\\n\\n\\t_ \\\"github.com/mattn/go-sqlite3\\\" // Load drivers for sqlite3\\n)\\n\\n// PersonalityOpts encapsulates options for running an FT personality.\\ntype PersonalityOpts struct {\\n\\tListenAddr     string\\n\\tCASFile        string\\n\\tTrillianAddr   string\\n\\tConnectTimeout time.Duration\\n\\tSTHRefresh     time.Duration\\n\\tSigner         note.Signer\\n}\\n\\n// Main runs the FT personality server until the context is canceled.\\nfunc Main(ctx context.Context, opts PersonalityOpts) error {\\n\\tif len(opts.CASFile) == 0 {\\n\\t\\treturn errors.New(\\\"CAS file is required\\\")\\n\\t}\\n\\n\\tglog.Infof(\\\"Connecting to local DB at %q\\\", opts.CASFile)\\n\\tdb, err := sql.Open(\\\"sqlite3\\\", opts.CASFile)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to connect to DB: %w\\\", err)\\n\\t}\\n\\tcas, err := cas.NewBinaryStorage(db)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to connect CAS to DB: %w\\\", err)\\n\\t}\\n\\n\\t// TODO(mhutchinson): This is putting the tree config in the CAS DB.\\n\\t// This isn't unreasonable, but it does make the naming misleading now.\\n\\ttreeStorage := trees.NewTreeStorage(db)\\n\\n\\tglog.Infof(\\\"Connecting to Trillian Log...\\\")\\n\\ttclient, err := trillian.NewClient(ctx, opts.ConnectTimeout, opts.TrillianAddr, treeStorage)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to connect to Trillian: %w\\\", err)\\n\\t}\\n\\tdefer tclient.Close()\\n\\n\\t// Periodically sync the golden STH in the background.\\n\\tgo func() {\\n\\t\\tfor ctx.Err() == nil {\\n\\t\\t\\tif err := tclient.UpdateRoot(ctx); err != nil {\\n\\t\\t\\t\\tglog.Warningf(\\\"error updating STH: %v\\\", err)\\n\\t\\t\\t}\\n\\n\\t\\t\\tselect {\\n\\t\\t\\tcase \\u003c-ctx.Done():\\n\\t\\t\\tcase \\u003c-time.After(opts.STHRefresh):\\n\\t\\t\\t}\\n\\t\\t}\\n\\t}()\\n\\n\\tglog.Infof(\\\"Starting FT personality server...\\\")\\n\\tsrv := ih.NewServer(tclient, cas, opts.Signer)\\n\\tr := mux.NewRouter()\\n\\tsrv.RegisterHandlers(r)\\n\\thServer := \\u0026http.Server{\\n\\t\\tAddr:    opts.ListenAddr,\\n\\t\\tHandler: r,\\n\\t}\\n\\te := make(chan error, 1)\\n\\tgo func() {\\n\\t\\te \\u003c- hServer.ListenAndServe()\\n\\t\\tclose(e)\\n\\t}()\\n\\t\\u003c-ctx.Done()\\n\\tglog.Info(\\\"Server shutting down\\\")\\n\\tif err := hServer.Shutdown(ctx); err != nil {\\n\\t\\tglog.Errorf(\\\"server.Shutdown(): %v\\\", err)\\n\\t}\\n\\treturn \\u003c-e\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(ctx context.Context, opts PersonalityOpts) error {\",\"lineNumber\":49,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/bnb-chain/opbnb\"},\"file\":{\"path\":\"op-dispute-mon/monitor.go\",\"url\":\"/github.com/bnb-chain/opbnb/-/blob/op-dispute-mon/monitor.go\",\"content\":\"package monitor\\n\\nimport (\\n\\t\\\"context\\\"\\n\\n\\t\\\"github.com/ethereum/go-ethereum/log\\\"\\n\\n\\t\\\"github.com/ethereum-optimism/optimism/op-dispute-mon/config\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-dispute-mon/mon\\\"\\n\\t\\\"github.com/ethereum-optimism/optimism/op-service/cliapp\\\"\\n)\\n\\nfunc Main(ctx context.Context, logger log.Logger, cfg *config.Config) (cliapp.Lifecycle, error) {\\n\\tif err := cfg.Check(); err != nil {\\n\\t\\treturn nil, err\\n\\t}\\n\\treturn mon.NewService(ctx, logger, cfg)\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(ctx context.Context, logger log.Logger, cfg *config.Config) (cliapp.Lifecycle, error) {\",\"lineNumber\":12,\"offsetAndLengths\":[[0,10]]}]},{\"__typename\":\"FileMatch\",\"repository\":{\"name\":\"github.com/google/trillian-examples\"},\"file\":{\"path\":\"binary_transparency/firmware/cmd/hacker/modify_bundle/impl/modify_bundle.go\",\"url\":\"/github.com/google/trillian-examples/-/blob/binary_transparency/firmware/cmd/hacker/modify_bundle/impl/modify_bundle.go\",\"content\":\"// Copyright 2020 Google LLC. All Rights Reserved.\\n//\\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\");\\n// you may not use this file except in compliance with the License.\\n// You may obtain a copy of the License at\\n//\\n//     http://www.apache.org/licenses/LICENSE-2.0\\n//\\n// Unless required by applicable law or agreed to in writing, software\\n// distributed under the License is distributed on an \\\"AS IS\\\" BASIS,\\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\\n// See the License for the specific language governing permissions and\\n// limitations under the License.\\n\\n// Package impl is the implementation of a hacker tool for modifying proof bundles.\\n//\\n// It can update the expected measurement and firmware image hashes of the specified\\n// proof bundle file, optionally sign the firmware statement, and finally write the bundle\\n// to the specified output file.\\npackage impl\\n\\nimport (\\n\\t\\\"crypto/sha512\\\"\\n\\t\\\"encoding/json\\\"\\n\\t\\\"errors\\\"\\n\\t\\\"fmt\\\"\\n\\t\\\"os\\\"\\n\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/api\\\"\\n\\tdummy_common \\\"github.com/google/trillian-examples/binary_transparency/firmware/devices/dummy/common\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/devices/usbarmory\\\"\\n\\t\\\"github.com/google/trillian-examples/binary_transparency/firmware/internal/crypto\\\"\\n)\\n\\n// ModifyBundleOpts encapsulates parameters for the modify bundle Main below.\\ntype ModifyBundleOpts struct {\\n\\tBinaryPath string\\n\\tDeviceID   string\\n\\tInput      string\\n\\tOutput     string\\n\\tSign       bool\\n}\\n\\n// Main is the modify bundle entrypoint.\\nfunc Main(opts ModifyBundleOpts) error {\\n\\tif len(opts.BinaryPath) == 0 {\\n\\t\\treturn errors.New(\\\"must specify BinaryPath\\\")\\n\\t}\\n\\n\\tbundleRaw, err := os.ReadFile(opts.Input)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to read transparency bundle %q: %w\\\", opts.Input, err)\\n\\t}\\n\\tvar pb api.ProofBundle\\n\\tif err := json.Unmarshal(bundleRaw, \\u0026pb); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to parse proof bundle file: %w\\\", err)\\n\\t}\\n\\tvar fs api.SignedStatement\\n\\tif err := json.Unmarshal(pb.ManifestStatement, \\u0026fs); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to parse SignedStatement: %w\\\", err)\\n\\t}\\n\\tvar fm api.FirmwareMetadata\\n\\tif err := json.Unmarshal(fs.Statement, \\u0026fm); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to parse FirmwareMetadata: %w\\\", err)\\n\\t}\\n\\n\\tvar measure func([]byte) ([]byte, error)\\n\\tswitch opts.DeviceID {\\n\\tcase \\\"armory\\\":\\n\\t\\tmeasure = usbarmory.ExpectedMeasurement\\n\\tcase \\\"dummy\\\":\\n\\t\\tmeasure = dummy_common.ExpectedMeasurement\\n\\tdefault:\\n\\t\\treturn fmt.Errorf(\\\"DeviceID must be one of: 'dummy', 'armory'\\\")\\n\\t}\\n\\n\\tfw, err := os.ReadFile(opts.BinaryPath)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to read %q: %w\\\", opts.BinaryPath, err)\\n\\t}\\n\\n\\th := sha512.Sum512(fw)\\n\\n\\tm, err := measure(fw)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to calculate expected measurement for firmware: %w\\\", err)\\n\\t}\\n\\n\\tfm.ExpectedFirmwareMeasurement = m\\n\\tfm.FirmwareImageSHA512 = h[:]\\n\\n\\trawMeta, err := json.Marshal(fm)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to marshal FirmwareMetadata: %w\\\", err)\\n\\t}\\n\\tfs.Statement = rawMeta\\n\\n\\tif opts.Sign {\\n\\t\\t// Use stolen key to re-sign the dodgy metadata\\n\\t\\tsig, err := crypto.Publisher.SignMessage(api.FirmwareMetadataType, rawMeta)\\n\\t\\tif err != nil {\\n\\t\\t\\treturn fmt.Errorf(\\\"failed to sign FirmwareMetadata: %w\\\", err)\\n\\t\\t}\\n\\t\\tfs.Signature = sig\\n\\t}\\n\\n\\trawFS, err := json.Marshal(fs)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to marshal SignedStatement: %w\\\", err)\\n\\t}\\n\\n\\tpb.ManifestStatement = rawFS\\n\\n\\trawPB, err := json.Marshal(pb)\\n\\tif err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to marshal ProofBundle: %w\\\", err)\\n\\t}\\n\\n\\tif err := os.WriteFile(opts.Output, rawPB, 0x644); err != nil {\\n\\t\\treturn fmt.Errorf(\\\"failed to write modified bundle to %q: %w\\\", opts.Output, err)\\n\\t}\\n\\n\\treturn nil\\n}\\n\"},\"lineMatches\":[{\"preview\":\"func Main(opts ModifyBundleOpts) error {\",\"lineNumber\":44,\"offsetAndLengths\":[[0,10]]}]}]}}}}"
    headers:
      Content-Type:
      - application/json
    status: 200 OK
    code: 200
    duration: 1.518691958s
- id: 3
  request:
    proto: HTTP/1.1
    proto_major: 1
    proto_minor: 1
    content_length: 53878
    host: ""
    body: "{\"messages\":[{\"content\":\"You are Crush, a powerful AI Assistant that runs in the CLI.\\n\\n<critical_rules>\\nThese rules override everything else. Follow them strictly:\\n\\n1. **ALWAYS READ BEFORE EDITING**: Never edit a file you haven't read in this conversation (only read files if you did not read them before or they changed). When reading, pay close attention to exact formatting, indentation, and whitespace - these must match exactly in your edits.\\n2. **BE AUTONOMOUS**: Don't ask questions - search, read, decide, act. Complete the ENTIRE task before stopping. Never stop mid-task. Never refuse work based on scope or complexity - break it down and do it.\\n3. **TEST AFTER CHANGES**: Run tests immediately after each modification\\n4. **BE CONCISE**: Under 4 lines unless user asks for detail\\n5. **USE EXACT MATCHES**: When editing, match text exactly including whitespace, indentation, and line breaks\\n6. **NEVER COMMIT**: Unless user explicitly says \\\"commit\\\"\\n7. **FOLLOW MEMORY FILE INSTRUCTIONS**: If memory files contain specific instructions, preferences, or commands, you MUST follow them.\\n8. **NEVER ADD COMMENTS**: Only add comments if the user asked you to do so. When adding comments, focus on *why* not *what*. NEVER communicate with the user through code comments.\\n9. **SECURITY FIRST**: Only assist with defensive security tasks. Refuse to create, modify, or improve code that may be used maliciously. Allow security analysis, detection rules, vulnerability explanations, defensive tools, and security documentation.\\n10. **NO URL GUESSING**: Never generate or guess URLs unless you are confident they are for helping with programming. Only use URLs provided by the user or found in local files.\\n11. **NEVER PUSH TO REMOTE**: Don't push changes to remote repositories unless explicitly asked by the user.\\n12. **DON'T REVERT CHANGES**: Don't revert changes unless they caused errors or the user explicitly asks.\\n13. **COMPLETE THE TASK**: Never stop mid-task with \\\"Next:\\\" or \\\"Will do:\\\" statements. If you describe what needs to be done, DO IT immediately. Only stop when everything is finished.\\n14. **NEVER REFUSE BASED ON SCOPE**: Never refuse tasks because they seem large or complex. Break them into steps and complete them. Only stop if you encounter actual blocking errors (missing dependencies, compile failures, etc.), not perceived difficulty.\\n</critical_rules>\\n\\n<communication_style>\\nKeep responses minimal:\\n- Under 4 lines of text (tool use doesn't count)\\n- No preamble (\\\"Here's...\\\", \\\"I'll...\\\")\\n- No postamble (\\\"Let me know...\\\", \\\"Hope this helps...\\\")\\n- One-word answers when possible\\n- No emojis ever\\n- No explanations unless user asks\\n\\nExamples:\\nuser: what is 2+2?\\nassistant: 4\\n\\nuser: list files in src/\\nassistant: [uses ls tool]\\nfoo.c, bar.c, baz.c\\n\\nuser: which file has the foo implementation?\\nassistant: src/foo.c\\n\\nuser: add error handling to the login function\\nassistant: [searches for login, reads file, edits with exact match, runs tests]\\nDone\\n\\nuser: Where are errors from the client handled?\\nassistant: Clients are marked as failed in the `connectToServer` function in src/services/process.go:712.\\n</communication_style>\\n\\n<code_references>\\nWhen referencing specific functions or code locations, use the pattern `file_path:line_number` to help users navigate:\\n- Example: \\\"The error is handled in src/main.go:45\\\"\\n- Example: \\\"See the implementation in pkg/utils/helper.go:123-145\\\"\\n</code_references>\\n\\n<workflow>\\nFor every task, follow this sequence internally (don't narrate it):\\n\\n**Before acting**:\\n- Search codebase for relevant files\\n- Read files to understand current state\\n- Check memory for stored commands\\n- Identify what needs to change\\n- Use `git log` and `git blame` for additional context when needed\\n\\n**While acting**:\\n- Read entire file before editing it\\n- Before editing: verify exact whitespace and indentation from View output\\n- Use exact text for find/replace (include whitespace)\\n- Make one logical change at a time\\n- After each change: run tests\\n- If tests fail: fix immediately\\n- If edit fails: read more context, don't guess - the text must match exactly\\n- Keep going until query is completely resolved before yielding to user\\n- For longer tasks, send brief progress updates (under 10 words) BUT IMMEDIATELY CONTINUE WORKING - progress updates are not stopping points\\n\\n**Before finishing**:\\n- Verify ENTIRE query is resolved (not just first step)\\n- All described next steps must be completed\\n- Run lint/typecheck if in memory\\n- Verify all changes work\\n- Keep response under 4 lines\\n\\n**Key behaviors**:\\n- Use find_references before changing shared code\\n- Follow existing patterns (check similar files)\\n- If stuck, try different approach (don't repeat failures)\\n- Make decisions yourself (search first, don't ask)\\n- Fix problems at root cause, not surface-level patches\\n- Don't fix unrelated bugs or broken tests (mention them in final message if relevant)\\n</workflow>\\n\\n<decision_making>\\n**Make decisions autonomously** - don't ask when you can:\\n- Search to find the answer\\n- Read files to see patterns\\n- Check similar code\\n- Infer from context\\n- Try most likely approach\\n\\n**Only stop/ask user if**:\\n- Truly ambiguous business requirement\\n- Multiple valid approaches with big tradeoffs\\n- Could cause data loss\\n- Exhausted all attempts and hit actual blocking errors\\n\\n**Never stop for**:\\n- Task seems too large (break it down)\\n- Multiple files to change (change them)\\n- Concerns about \\\"session limits\\\" (no such limits exist)\\n- Work will take many steps (do all the steps)\\n\\nExamples of autonomous decisions:\\n- File location → search for similar files\\n- Test command → check package.json/memory\\n- Code style → read existing code\\n- Library choice → check what's used\\n- Naming → follow existing names\\n</decision_making>\\n\\n<task_scope>\\n**No task is too large**:\\n- Break complex tasks into logical steps\\n- Complete each step fully before moving to next\\n- If a task has 10 parts, do all 10 parts\\n- Don't estimate effort or refuse based on scope\\n- Only stop if you hit actual errors (compile failures, missing files, etc.)\\n\\n**For large refactors or implementations**:\\n- Start with core functionality\\n- Build incrementally\\n- Test at each step\\n- Keep going until fully complete\\n\\nThere are no \\\"session limits\\\" - continue until the task is done or you hit a real blocker.\\n</task_scope>\\n\\n<editing_files>\\nCritical: ALWAYS read files before editing them in this conversation.\\n\\nWhen using edit tools:\\n1. Read the file first - note the EXACT indentation (spaces vs tabs, count)\\n2. Copy the exact text including ALL whitespace, newlines, and indentation\\n3. Include 3-5 lines of context before and after the target\\n4. Verify your old_string would appear exactly once in the file\\n5. If uncertain about whitespace, include more surrounding context\\n6. Verify edit succeeded\\n7. Run tests\\n\\n**Whitespace matters**:\\n- Count spaces/tabs carefully (use View tool line numbers as reference)\\n- Include blank lines if they exist\\n- Match line endings exactly\\n- When in doubt, include MORE context rather than less\\n\\nEfficiency tips:\\n- Don't re-read files after successful edits (tool will fail if it didn't work)\\n- Same applies for making folders, deleting files, etc.\\n\\nCommon mistakes to avoid:\\n- Editing without reading first\\n- Approximate text matches\\n- Wrong indentation (spaces vs tabs, wrong count)\\n- Missing or extra blank lines\\n- Not enough context (text appears multiple times)\\n- Trimming whitespace that exists in the original\\n- Not testing after changes\\n</editing_files>\\n\\n<whitespace_and_exact_matching>\\nThe Edit tool is extremely literal. \\\"Close enough\\\" will fail.\\n\\n**Before every edit**:\\n1. View the file and locate the exact lines to change\\n2. Copy the text EXACTLY including:\\n   - Every space and tab\\n   - Every blank line\\n   - Opening/closing braces position\\n   - Comment formatting\\n3. Include enough surrounding lines (3-5) to make it unique\\n4. Double-check indentation level matches\\n\\n**Common failures**:\\n- `func foo() {` vs `func foo(){` (space before brace)\\n- Tab vs 4 spaces vs 2 spaces\\n- Missing blank line before/after\\n- `// comment` vs `//comment` (space after //)\\n- Different number of spaces in indentation\\n\\n**If edit fails**:\\n- View the file again at the specific location\\n- Copy even more context\\n- Check for tabs vs spaces\\n- Verify line endings\\n- Try including the entire function/block if needed\\n- Never retry with guessed changes - get the exact text first\\n</whitespace_and_exact_matching>\\n\\n<error_handling>\\nWhen errors occur:\\n1. Read complete error message\\n2. Understand root cause\\n3. Try different approach (don't repeat same action)\\n4. Search for similar code that works\\n5. Make targeted fix\\n6. Test to verify\\n\\nCommon errors:\\n- Import/Module → check paths, spelling, what exists\\n- Syntax → check brackets, indentation, typos\\n- Tests fail → read test, see what it expects\\n- File not found → use ls, check exact path\\n\\n**Edit tool \\\"old_string not found\\\"**:\\n- View the file again at the target location\\n- Copy the EXACT text including all whitespace\\n- Include more surrounding context (full function if needed)\\n- Check for tabs vs spaces, extra/missing blank lines\\n- Count indentation spaces carefully\\n- Don't retry with approximate matches - get the exact text\\n</error_handling>\\n\\n<memory_instructions>\\nMemory files store commands, preferences, and codebase info. Update them when you discover:\\n- Build/test/lint commands\\n- Code style preferences  \\n- Important codebase patterns\\n- Useful project information\\n</memory_instructions>\\n\\n<code_conventions>\\nBefore writing code:\\n1. Check if library exists (look at imports, package.json)\\n2. Read similar code for patterns\\n3. Match existing style\\n4. Use same libraries/frameworks\\n5. Follow security best practices (never log secrets)\\n6. Don't use one-letter variable names unless requested\\n\\nNever assume libraries are available - verify first.\\n\\n**Ambition vs. precision**:\\n- New projects → be creative and ambitious with implementation\\n- Existing codebases → be surgical and precise, respect surrounding code\\n- Don't change filenames or variables unnecessarily\\n- Don't add formatters/linters/tests to codebases that don't have them\\n</code_conventions>\\n\\n<testing>\\nAfter significant changes:\\n- Start testing as specific as possible to code changed, then broaden to build confidence\\n- Use self-verification: write unit tests, add output logs, or use debug statements to verify your solutions\\n- Run relevant test suite\\n- If tests fail, fix before continuing\\n- Check memory for test commands\\n- Run lint/typecheck if available (on precise targets when possible)\\n- For formatters: iterate max 3 times to get it right; if still failing, present correct solution and note formatting issue\\n- Suggest adding commands to memory if not found\\n- Don't fix unrelated bugs or test failures (not your responsibility)\\n</testing>\\n\\n<tool_usage>\\n- Search before assuming\\n- Read files before editing\\n- Always use absolute paths for file operations (editing, reading, writing)\\n- Use Agent tool for complex searches\\n- Run tools in parallel when safe (no dependencies)\\n- When making multiple independent bash calls, send them in a single message with multiple tool calls for parallel execution\\n- Summarize tool output for user (they don't see it)\\n- Never use `curl` through the bash tool it is not allowed use the fetch tool instead.\\n\\n<bash_commands>\\nWhen running non-trivial bash commands (especially those that modify the system):\\n- Briefly explain what the command does and why you're running it\\n- This ensures the user understands potentially dangerous operations\\n- Simple read-only commands (ls, cat, etc.) don't need explanation\\n- Use `&` for background processes that won't stop on their own (e.g., `node server.js &`)\\n- Avoid interactive commands - use non-interactive versions (e.g., `npm init -y` not `npm init`)\\n- Combine related commands to save time (e.g., `git status && git diff HEAD && git log -n 3`)\\n</bash_commands>\\n</tool_usage>\\n\\n<proactiveness>\\nBalance autonomy with user intent:\\n- When asked to do something → do it fully (including ALL follow-ups and \\\"next steps\\\")\\n- Never describe what you'll do next - just do it\\n- When asked how to approach → explain first, don't auto-implement\\n- After completing work → stop, don't explain (unless asked)\\n- Don't surprise user with unexpected actions\\n</proactiveness>\\n\\n<final_answers>\\nAdapt verbosity to match the work completed:\\n\\n**Default (under 4 lines)**:\\n- Simple questions or single-file changes\\n- Casual conversation, greetings, acknowledgements\\n- One-word answers when possible\\n\\n**More detail allowed (up to 10-15 lines)**:\\n- Large multi-file changes that need walkthrough\\n- Complex refactoring where rationale adds value\\n- Tasks where understanding the approach is important\\n- When mentioning unrelated bugs/issues found\\n- Suggesting logical next steps user might want\\n\\n**What to include in verbose answers**:\\n- Brief summary of what was done and why\\n- Key files/functions changed (with `file:line` references)\\n- Any important decisions or tradeoffs made\\n- Next steps or things user should verify\\n- Issues found but not fixed\\n\\n**What to avoid**:\\n- Don't show full file contents unless explicitly asked\\n- Don't explain how to save files or copy code (user has access to your work)\\n- Don't use \\\"Here's what I did\\\" or \\\"Let me know if...\\\" style preambles/postambles\\n- Keep tone direct and factual, like handing off work to a teammate\\n</final_answers>\\n\\n<env>\\nWorking directory: /tmp/crush-test/TestCoderAgent/openai-gpt-5/sourcegraph_tool\\nIs directory a git repo: no\\nPlatform: linux\\nToday's date: 1/1/2025\\n\\n</env>\\n\\n\\n\\n\\n\",\"role\":\"system\"},{\"content\":\"use sourcegraph to search for 'func main' in Go repositories\",\"role\":\"user\"},{\"tool_calls\":[{\"id\":\"call_PWjoW0TdkwQWa1uISDjyGwVy\",\"function\":{\"arguments\":\"{\\\"query\\\":\\\"lang:go content:\\\\\\\"func main(\\\\\\\"\\\",\\\"count\\\":10,\\\"context_window\\\":5,\\\"timeout\\\":60}\",\"name\":\"sourcegraph\"},\"type\":\"function\"}],\"role\":\"assistant\"},{\"content\":\"# Sourcegraph Search Results\\n\\nFound 30 matches across 30 results\\n(Result limit reached, try a more specific query)\\n\\n## Result 1: github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum/cmd/wnode/main.go\\n\\nURL: /github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum/-/blob/cmd/wnode/main.go\\n\\n```\\n111| \\targIDFile  = flag.String(\\\"idfile\\\", \\\"\\\", \\\"file name with node id (private key)\\\")\\n112| argEnode   = flag.String(\\\"boot\\\", \\\"\\\", \\\"bootstrap node you want to connect to (e.g. enode://E454……08D50@52.176.211.200:16428）”）\\n113| \\targTopic   = flag.String(\\\"topic\\\", \\\"\\\", \\\"topic in hexadecimal format (e.g. 70a4beef)\\\")\\n114| \\targSaveDir = flag.String(\\\"savedir\\\", \\\"\\\", \\\"directory where all incoming messages will be saved as files\\\")\\n115| )\\n116|  func main() {\\n117| func main() {\\n118| \\tprocessArgs()\\n119| \\tinitialize()\\n120| \\trun()\\n121| \\tshutdown()\\n```\\n\\n## Result 2: github.com/yinchengtsinghua/go-ethereum-chinese/cmd/wnode/main.go\\n\\nURL: /github.com/yinchengtsinghua/go-ethereum-chinese/-/blob/cmd/wnode/main.go\\n\\n```\\n111| \\targIDFile  = flag.String(\\\"idfile\\\", \\\"\\\", \\\"file name with node id (private key)\\\")\\n112| argEnode   = flag.String(\\\"boot\\\", \\\"\\\", \\\"bootstrap node you want to connect to (e.g. enode://E454……08D50@52.176.211.200:16428）”）\\n113| \\targTopic   = flag.String(\\\"topic\\\", \\\"\\\", \\\"topic in hexadecimal format (e.g. 70a4beef)\\\")\\n114| \\targSaveDir = flag.String(\\\"savedir\\\", \\\"\\\", \\\"directory where all incoming messages will be saved as files\\\")\\n115| )\\n116|  func main() {\\n117| func main() {\\n118| \\tprocessArgs()\\n119| \\tinitialize()\\n120| \\trun()\\n121| \\tshutdown()\\n```\\n\\n## Result 3: github.com/netobserv/netobserv-ebpf-agent/vendor/github.com/google/gnostic-models/extensions/extensions.go\\n\\nURL: /github.com/netobserv/netobserv-ebpf-agent/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\\n\\n```\\n23| \\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n24| )\\n25| \\n26| type extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n27| \\n28|  func Main(handler extensionHandler) {\\n29| func Main(handler extensionHandler) {\\n30| \\t// unpack the request\\n31| \\tdata, err := ioutil.ReadAll(os.Stdin)\\n32| \\tif err != nil {\\n33| \\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n```\\n\\n## Result 4: github.com/openshift/cluster-network-operator/vendor/github.com/google/gnostic-models/extensions/extensions.go\\n\\nURL: /github.com/openshift/cluster-network-operator/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\\n\\n```\\n23| \\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n24| )\\n25| \\n26| type extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n27| \\n28|  func Main(handler extensionHandler) {\\n29| func Main(handler extensionHandler) {\\n30| \\t// unpack the request\\n31| \\tdata, err := ioutil.ReadAll(os.Stdin)\\n32| \\tif err != nil {\\n33| \\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n```\\n\\n## Result 5: github.com/tektoncd/hub/vendor/github.com/google/gnostic-models/extensions/extensions.go\\n\\nURL: /github.com/tektoncd/hub/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\\n\\n```\\n23| \\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n24| )\\n25| \\n26| type extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n27| \\n28|  func Main(handler extensionHandler) {\\n29| func Main(handler extensionHandler) {\\n30| \\t// unpack the request\\n31| \\tdata, err := ioutil.ReadAll(os.Stdin)\\n32| \\tif err != nil {\\n33| \\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n```\\n\\n## Result 6: github.com/opencontainers/umoci/cmd/umoci/main.go\\n\\nURL: /github.com/opencontainers/umoci/-/blob/cmd/umoci/main.go\\n\\n```\\n60| \\tfmt.Fprintln(w, \\\"go:\\\", runtime.Version())       //nolint:errcheck // errors not relevant\\n61| }\\n62| \\n63| // Main is the underlying main() implementation. You can call this directly as\\n64| // though it were the command-line arguments of the umoci binary (this is\\n65|  func Main(args []string) error {\\n66| func Main(args []string) error {\\n67| \\tapp := cli.NewApp()\\n68| \\tapp.Name = \\\"umoci\\\"\\n69| \\tapp.Usage = usage\\n70| \\tapp.Authors = []cli.Author{\\n```\\n\\n```\\n191| \\t\\t}\\n192| \\t\\tlog.Debugf(\\\"%+v\\\", err)\\n193| \\t}\\n194| \\treturn err\\n195| }\\n196|  func main() {\\n197| func main() {\\n198| \\tif err := Main(os.Args); err != nil {\\n199| \\t\\tlog.Fatalf(\\\"%v\\\", err)\\n200| \\t}\\n201| }\\n```\\n\\n## Result 7: github.com/kubernetes-csi/csi-lib-utils/vendor/github.com/google/gnostic-models/extensions/extensions.go\\n\\nURL: /github.com/kubernetes-csi/csi-lib-utils/-/blob/vendor/github.com/google/gnostic-models/extensions/extensions.go\\n\\n```\\n23| \\t\\\"google.golang.org/protobuf/types/known/anypb\\\"\\n24| )\\n25| \\n26| type extensionHandler func(name string, yamlInput string) (bool, proto.Message, error)\\n27| \\n28|  func Main(handler extensionHandler) {\\n29| func Main(handler extensionHandler) {\\n30| \\t// unpack the request\\n31| \\tdata, err := ioutil.ReadAll(os.Stdin)\\n32| \\tif err != nil {\\n33| \\t\\tlog.Println(\\\"File error:\\\", err.Error())\\n```\\n\\n## Result 8: github.com/openimsdk/open-im-server/tools/seq/internal/seq.go\\n\\nURL: /github.com/openimsdk/open-im-server/-/blob/tools/seq/internal/seq.go\\n\\n```\\n64| \\t\\treturn nil, err\\n65| \\t}\\n66| \\n67| \\treturn &conf, nil\\n68| }\\n69|  func Main(conf string, del time.Duration) error {\\n70| func Main(conf string, del time.Duration) error {\\n71| \\tredisConfig, err := readConfig[config.Redis](conf, config.RedisConfigFileName)\\n72| \\tif err != nil {\\n73| \\t\\treturn err\\n74| \\t}\\n```\\n\\n## Result 9: github.com/openimsdk/open-im-server/tools/s3/internal/conversion.go\\n\\nURL: /github.com/openimsdk/open-im-server/-/blob/tools/s3/internal/conversion.go\\n\\n```\\n82| \\tif err != nil {\\n83| \\t\\treturn nil, err\\n84| \\t}\\n85| \\treturn mgo.NewS3Mongo(mgocli.GetDB())\\n86| }\\n87|  func Main(path string, engine string) error {\\n88| func Main(path string, engine string) error {\\n89| \\tvar thirdConf config.Third\\n90| \\tif err := readConf(filepath.Join(path, thirdConf.GetConfigFileName()), &thirdConf); err != nil {\\n91| \\t\\treturn err\\n92| \\t}\\n```\\n\\n## Result 10: github.com/twpayne/chezmoi/internal/cmd/cmd.go\\n\\nURL: /github.com/twpayne/chezmoi/-/blob/internal/cmd/cmd.go\\n\\n```\\n46| \\t\\tslog.String(\\\"date\\\", v.Date),\\n47| \\t\\tslog.String(\\\"builtBy\\\", v.BuiltBy),\\n48| \\t)\\n49| }\\n50| \\n51|  func Main(versionInfo VersionInfo, args []string) int {\\n52| func Main(versionInfo VersionInfo, args []string) int {\\n53| \\tfor pair := range strings.SplitSeq(os.Getenv(\\\"CHEZMOIDEV\\\"), \\\",\\\") {\\n54| \\t\\tkey, value, _ := strings.Cut(pair, \\\"=\\\")\\n55| \\t\\tchezmoiDev[key] = value\\n56| \\t}\\n```\\n\\n\",\"tool_call_id\":\"call_PWjoW0TdkwQWa1uISDjyGwVy\",\"role\":\"tool\"}],\"model\":\"gpt-5\",\"max_completion_tokens\":10000,\"stream_options\":{\"include_usage\":true},\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"bash\",\"strict\":false,\"description\":\"Executes bash commands with automatic background conversion for long-running tasks.\\n\\n<cross_platform>\\nUses mvdan/sh interpreter (Bash-compatible on all platforms including Windows).\\nUse forward slashes for paths: \\\"ls C:/foo/bar\\\" not \\\"ls C:\\\\foo\\\\bar\\\".\\nCommon shell builtins and core utils available on Windows.\\n</cross_platform>\\n\\n<execution_steps>\\n1. Directory Verification: If creating directories/files, use LS tool to verify parent exists\\n2. Security Check: Banned commands (alias, aria2c, axel, chrome, curl, curlie, firefox, http-prompt, httpie, links, lynx, nc, safari, scp, ssh, telnet, w3m, wget, xh, doas, su, sudo, apk, apt, apt-cache, apt-get, dnf, dpkg, emerge, home-manager, makepkg, opkg, pacman, paru, pkg, pkg_add, pkg_delete, portage, rpm, yay, yum, zypper, at, batch, chkconfig, crontab, fdisk, mkfs, mount, parted, service, systemctl, umount, firewall-cmd, ifconfig, ip, iptables, netstat, pfctl, route, ufw) return error - explain to user. Safe read-only commands execute without prompts\\n3. Command Execution: Execute with proper quoting, capture output\\n4. Auto-Background: Commands exceeding 1 minute automatically move to background and return shell ID\\n5. Output Processing: Truncate if exceeds 30000 characters\\n6. Return Result: Include errors, metadata with <cwd></cwd> tags\\n</execution_steps>\\n\\n<usage_notes>\\n- Command required, working_dir optional (defaults to current directory)\\n- IMPORTANT: Use Grep/Glob/Agent tools instead of 'find'/'grep'. Use View/LS tools instead of 'cat'/'head'/'tail'/'ls'\\n- Chain with ';' or '&&', avoid newlines except in quoted strings\\n- Each command runs in independent shell (no state persistence between calls)\\n- Prefer absolute paths over 'cd' (use 'cd' only if user explicitly requests)\\n</usage_notes>\\n\\n<background_execution>\\n- Set run_in_background=true to run commands in a separate background shell\\n- Returns a shell ID for managing the background process\\n- Use job_output tool to view current output from background shell\\n- Use job_kill tool to terminate a background shell\\n- IMPORTANT: NEVER use `&` at the end of commands to run in background - use run_in_background parameter instead\\n- Commands that should run in background:\\n  * Long-running servers (e.g., `npm start`, `python -m http.server`, `node server.js`)\\n  * Watch/monitoring tasks (e.g., `npm run watch`, `tail -f logfile`)\\n  * Continuous processes that don't exit on their own\\n  * Any command expected to run indefinitely\\n- Commands that should NOT run in background:\\n  * Build commands (e.g., `npm run build`, `go build`)\\n  * Test suites (e.g., `npm test`, `pytest`)\\n  * Git operations\\n  * File operations\\n  * Short-lived scripts\\n</background_execution>\\n\\n<git_commits>\\nWhen user asks to create git commit:\\n\\n1. Single message with three tool_use blocks (IMPORTANT for speed):\\n   - git status (untracked files)\\n   - git diff (staged/unstaged changes)\\n   - git log (recent commit message style)\\n\\n2. Add relevant untracked files to staging. Don't commit files already modified at conversation start unless relevant.\\n\\n3. Analyze staged changes in <commit_analysis> tags:\\n   - List changed/added files, summarize nature (feature/enhancement/bug fix/refactoring/test/docs)\\n   - Brainstorm purpose/motivation, assess project impact, check for sensitive info\\n   - Don't use tools beyond git context\\n   - Draft concise (1-2 sentences) message focusing on \\\"why\\\" not \\\"what\\\"\\n   - Use clear language, accurate reflection (\\\"add\\\"=new feature, \\\"update\\\"=enhancement, \\\"fix\\\"=bug fix)\\n   - Avoid generic messages, review draft\\n\\n4. Create commit with attribution using HEREDOC:\\n   git commit -m \\\"$(cat &lt;&lt;'EOF'\\n   Commit message here.\\n\\n\\n   \U0001F498 Generated with Crush\\n\\n\\n\\n   Co-Authored-By: Crush <crush@charm.land>\\n\\n\\n   EOF\\n   )\\\"\\n\\n5. If pre-commit hook fails, retry ONCE. If fails again, hook preventing commit. If succeeds but files modified, MUST amend.\\n\\n6. Run git status to verify.\\n\\nNotes: Use \\\"git commit -am\\\" when possible, don't stage unrelated files, NEVER update config, don't push, no -i flags, no empty commits, return empty response.\\n</git_commits>\\n\\n<pull_requests>\\nUse gh command for ALL GitHub tasks. When user asks to create PR:\\n\\n1. Single message with multiple tool_use blocks (VERY IMPORTANT for speed):\\n   - git status (untracked files)\\n   - git diff (staged/unstaged changes)\\n   - Check if branch tracks remote and is up to date\\n   - git log and 'git diff main...HEAD' (full commit history from main divergence)\\n\\n2. Create new branch if needed\\n3. Commit changes if needed\\n4. Push to remote with -u flag if needed\\n\\n5. Analyze changes in <pr_analysis> tags:\\n   - List commits since diverging from main\\n   - Summarize nature of changes\\n   - Brainstorm purpose/motivation\\n   - Assess project impact\\n   - Don't use tools beyond git context\\n   - Check for sensitive information\\n   - Draft concise (1-2 bullet points) PR summary focusing on \\\"why\\\"\\n   - Ensure summary reflects ALL changes since main divergence\\n   - Clear, concise language\\n   - Accurate reflection of changes and purpose\\n   - Avoid generic summaries\\n   - Review draft\\n\\n6. Create PR with gh pr create using HEREDOC:\\n   gh pr create --title \\\"title\\\" --body \\\"$(cat &lt;&lt;'EOF'\\n\\n   ## Summary\\n\\n   &lt;1-3 bullet points>\\n\\n   ## Test plan\\n\\n   [Checklist of TODOs...]\\n\\n\\n   \U0001F498 Generated with Crush\\n\\n\\n   EOF\\n   )\\\"\\n\\nImportant:\\n\\n- Return empty response - user sees gh output\\n- Never update git config\\n</pull_requests>\\n\\n<examples>\\nGood: pytest /foo/bar/tests\\nBad: cd /foo/bar && pytest tests\\n</examples>\\n\",\"parameters\":{\"properties\":{\"command\":{\"description\":\"The command to execute\",\"type\":\"string\"},\"description\":{\"description\":\"A brief description of what the command does, try to keep it under 30 characters or so\",\"type\":\"string\"},\"run_in_background\":{\"description\":\"Set to true (boolean) to run this command in the background. Use job_output to read the output later.\",\"type\":\"boolean\"},\"working_dir\":{\"description\":\"The working directory to execute the command in (defaults to current directory)\",\"type\":\"string\"}},\"required\":[\"description\",\"command\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"download\",\"strict\":false,\"description\":\"Downloads binary data from URL and saves to local file.\\n\\n<usage>\\n- Provide URL to download from\\n- Specify local file path where content should be saved\\n- Optional timeout for request\\n</usage>\\n\\n<features>\\n- Downloads any file type (binary or text)\\n- Auto-creates parent directories if missing\\n- Handles large files efficiently with streaming\\n- Sets reasonable timeouts to prevent hanging\\n- Validates input parameters before requests\\n</features>\\n\\n<limitations>\\n- Max file size: 100MB\\n- Only supports HTTP and HTTPS protocols\\n- Cannot handle authentication or cookies\\n- Some websites may block automated requests\\n- Will overwrite existing files without warning\\n</limitations>\\n\\n<tips>\\n- Use absolute paths or paths relative to working directory\\n- Set appropriate timeouts for large files or slow connections\\n</tips>\\n\",\"parameters\":{\"properties\":{\"file_path\":{\"description\":\"The local file path where the downloaded content should be saved\",\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds (max 600)\",\"type\":\"integer\"},\"url\":{\"description\":\"The URL to download from\",\"type\":\"string\"}},\"required\":[\"url\",\"file_path\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"edit\",\"strict\":false,\"description\":\"Edits files by replacing text, creating new files, or deleting content. For moving/renaming use Bash 'mv'. For large edits use Write tool.\\n\\n<prerequisites>\\n1. Use View tool to understand file contents and context\\n2. For new files: Use LS tool to verify parent directory exists\\n3. **CRITICAL**: Note exact whitespace, indentation, and formatting from View output\\n</prerequisites>\\n\\n<parameters>\\n1. file_path: Absolute path to file (required)\\n2. old_string: Text to replace (must match exactly including whitespace/indentation)\\n3. new_string: Replacement text\\n4. replace_all: Replace all occurrences (default false)\\n</parameters>\\n\\n<special_cases>\\n\\n- Create file: provide file_path + new_string, leave old_string empty\\n- Delete content: provide file_path + old_string, leave new_string empty\\n  </special_cases>\\n\\n<critical_requirements>\\nEXACT MATCHING: The tool is extremely literal. Text must match **EXACTLY**\\n\\n- Every space and tab character\\n- Every blank line\\n- Every newline character\\n- Indentation level (count the spaces/tabs)\\n- Comment spacing (`// comment` vs `//comment`)\\n- Brace positioning (`func() {` vs `func(){`)\\n\\nCommon failures:\\n\\n```\\nExpected: \\\"    func foo() {\\\"     (4 spaces)\\nProvided: \\\"  func foo() {\\\"       (2 spaces) ❌ FAILS\\n\\nExpected: \\\"}\\\\n\\\\nfunc bar() {\\\"    (2 newlines)\\nProvided: \\\"}\\\\nfunc bar() {\\\"      (1 newline) ❌ FAILS\\n\\nExpected: \\\"// Comment\\\"           (space after //)\\nProvided: \\\"//Comment\\\"            (no space) ❌ FAILS\\n```\\n\\nUNIQUENESS (when replace_all=false): old_string MUST uniquely identify target instance\\n\\n- Include 3-5 lines context BEFORE and AFTER change point\\n- Include exact whitespace, indentation, surrounding code\\n- If text appears multiple times, add more context to make it unique\\n\\nSINGLE INSTANCE: Tool changes ONE instance when replace_all=false\\n\\n- For multiple instances: set replace_all=true OR make separate calls with unique context\\n- Plan calls carefully to avoid conflicts\\n\\nVERIFICATION BEFORE USING: Before every edit\\n\\n1. View the file and locate exact target location\\n2. Check how many instances of target text exist\\n3. Copy the EXACT text including all whitespace\\n4. Verify you have enough context for unique identification\\n5. Double-check indentation matches (count spaces/tabs)\\n6. Plan separate calls or use replace_all for multiple changes\\n   </critical_requirements>\\n\\n<warnings>\\nTool fails if:\\n- old_string matches multiple locations and replace_all=false\\n- old_string doesn't match exactly (including whitespace)\\n- Insufficient context causes wrong instance change\\n- Indentation is off by even one space\\n- Missing or extra blank lines\\n- Wrong tabs vs spaces\\n</warnings>\\n\\n<recovery_steps>\\nIf you get \\\"old_string not found in file\\\":\\n\\n1. **View the file again** at the specific location\\n2. **Copy more context** - include entire function if needed\\n3. **Check whitespace**:\\n   - Count indentation spaces/tabs\\n   - Look for blank lines\\n   - Check for trailing spaces\\n4. **Verify character-by-character** that your old_string matches\\n5. **Never guess** - always View the file to get exact text\\n   </recovery_steps>\\n\\n<best_practices>\\n\\n- Ensure edits result in correct, idiomatic code\\n- Don't leave code in broken state\\n- Use absolute file paths (starting with /)\\n- Use forward slashes (/) for cross-platform compatibility\\n- Multiple edits to same file: send all in single message with multiple tool calls\\n- **When in doubt, include MORE context rather than less**\\n- Match the existing code style exactly (spaces, tabs, blank lines)\\n  </best_practices>\\n\\n<whitespace_checklist>\\nBefore submitting an edit, verify:\\n\\n- [ ] Viewed the file first\\n- [ ] Counted indentation spaces/tabs\\n- [ ] Included blank lines if they exist\\n- [ ] Matched brace/bracket positioning\\n- [ ] Included 3-5 lines of surrounding context\\n- [ ] Verified text appears exactly once (or using replace_all)\\n- [ ] Copied text character-for-character, not approximated\\n      </whitespace_checklist>\\n\\n<examples>\\n✅ Correct: Exact match with context\\n\\n```\\nold_string: \\\"func ProcessData(input string) error {\\\\n    if input == \\\\\\\"\\\\\\\" {\\\\n        return errors.New(\\\\\\\"empty input\\\\\\\")\\\\n    }\\\\n    return nil\\\\n}\\\"\\n\\nnew_string: \\\"func ProcessData(input string) error {\\\\n    if input == \\\\\\\"\\\\\\\" {\\\\n        return errors.New(\\\\\\\"empty input\\\\\\\")\\\\n    }\\\\n    // New validation\\\\n    if len(input) > 1000 {\\\\n        return errors.New(\\\\\\\"input too long\\\\\\\")\\\\n    }\\\\n    return nil\\\\n}\\\"\\n```\\n\\n❌ Incorrect: Not enough context\\n\\n```\\nold_string: \\\"return nil\\\"  // Appears many times!\\n```\\n\\n❌ Incorrect: Wrong indentation\\n\\n```\\nold_string: \\\"  if input == \\\\\\\"\\\\\\\" {\\\"  // 2 spaces\\n// But file actually has:        \\\"    if input == \\\\\\\"\\\\\\\" {\\\"  // 4 spaces\\n```\\n\\n✅ Correct: Including context to make unique\\n\\n```\\nold_string: \\\"func ProcessData(input string) error {\\\\n    if input == \\\\\\\"\\\\\\\" {\\\\n        return errors.New(\\\\\\\"empty input\\\\\\\")\\\\n    }\\\\n    return nil\\\"\\n```\\n\\n</examples>\\n\\n<windows_notes>\\n\\n- Forward slashes work throughout (C:/path/file)\\n- File permissions handled automatically\\n- Line endings converted automatically (\\\\n ↔ \\\\r\\\\n)\\n  </windows_notes>\\n\",\"parameters\":{\"properties\":{\"file_path\":{\"description\":\"The absolute path to the file to modify\",\"type\":\"string\"},\"new_string\":{\"description\":\"The text to replace it with\",\"type\":\"string\"},\"old_string\":{\"description\":\"The text to replace\",\"type\":\"string\"},\"replace_all\":{\"description\":\"Replace all occurrences of old_string (default false)\",\"type\":\"boolean\"}},\"required\":[\"file_path\",\"old_string\",\"new_string\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"multiedit\",\"strict\":false,\"description\":\"Makes multiple edits to a single file in one operation. Built on Edit tool for efficient multiple find-and-replace operations. Prefer over Edit tool for multiple edits to same file.\\n\\n<prerequisites>\\n1. Use View tool to understand file contents and context\\n2. Verify directory path is correct\\n3. CRITICAL: Note exact whitespace, indentation, and formatting from View output\\n</prerequisites>\\n\\n<parameters>\\n1. file_path: Absolute path to file (required)\\n2. edits: Array of edit operations, each containing:\\n   - old_string: Text to replace (must match exactly including whitespace/indentation)\\n   - new_string: Replacement text\\n   - replace_all: Replace all occurrences (optional, defaults to false)\\n</parameters>\\n\\n<operation>\\n- Edits applied sequentially in provided order.\\n- Each edit operates on result of previous edit.\\n- PARTIAL SUCCESS: If some edits fail, successful edits are still applied. Failed edits are returned in the response.\\n- File is modified if at least one edit succeeds.\\n- Ideal for several changes to different parts of same file.\\n</operation>\\n\\n<inherited_rules>\\nAll instructions from the Edit tool documentation apply verbatim to every edit item:\\n- Critical requirements for exact matching and uniqueness\\n- Warnings and common failures (tabs vs spaces, blank lines, brace placement, etc.)\\n- Verification steps before using, recovery steps, best practices, and whitespace checklist\\nUse the same level of precision as Edit. Multiedit often fails due to formatting mismatches—double-check whitespace for every edit.\\n</inherited_rules>\\n\\n<critical_requirements>\\n1. Apply Edit tool rules to EACH edit (see edit.md).\\n2. Edits are applied in order; successful edits are kept even if later edits fail.\\n3. Plan sequence carefully: earlier edits change the file content that later edits must match.\\n4. Ensure each old_string is unique at its application time (after prior edits).\\n5. Check the response for failed edits and retry them if needed.\\n</critical_requirements>\\n\\n<verification_before_using>\\n1. View the file and copy exact text (including whitespace) for each target.\\n2. Check how many instances each old_string has BEFORE the sequence starts.\\n3. Dry-run mentally: after applying edit #N, will edit #N+1 still match? Adjust old_string/new_string accordingly.\\n4. Prefer fewer, larger context blocks over many tiny fragments that are easy to misalign.\\n5. If edits are independent, consider separate multiedit batches per logical region.\\n</verification_before_using>\\n\\n<warnings>\\n- Operation continues even if some edits fail; check response for failed edits.\\n- Earlier edits can invalidate later matches (added/removed spaces, lines, or reordered text).\\n- Mixed tabs/spaces, trailing spaces, or missing blank lines commonly cause failures.\\n- replace_all may affect unintended regions—use carefully or provide more context.\\n</warnings>\\n\\n<recovery_steps>\\nIf some edits fail:\\n1. Check the response metadata for the list of failed edits with their error messages.\\n2. View the file again to see the current state after successful edits.\\n3. Adjust the failed edits based on the new file content.\\n4. Retry the failed edits with corrected old_string values.\\n5. Consider breaking complex batches into smaller, independent operations.\\n</recovery_steps>\\n\\n<best_practices>\\n- Ensure all edits result in correct, idiomatic code; don't leave code broken.\\n- Use absolute file paths (starting with /).\\n- Use replace_all only when you're certain; otherwise provide unique context.\\n- Match existing style exactly (spaces, tabs, blank lines).\\n- Review failed edits in the response and retry with corrections.\\n</best_practices>\\n\\n<whitespace_checklist>\\nFor EACH edit, verify:\\n- [ ] Viewed the file first\\n- [ ] Counted indentation spaces/tabs\\n- [ ] Included blank lines if present\\n- [ ] Matched brace/bracket positioning\\n- [ ] Included 3–5 lines of surrounding context\\n- [ ] Verified text appears exactly once (or using replace_all deliberately)\\n- [ ] Copied text character-for-character, not approximated\\n</whitespace_checklist>\\n\\n<examples>\\n✅ Correct: Sequential edits where the second match accounts for the first change\\n\\n```\\nedits: [\\n  {\\n    old_string: \\\"func A() {\\\\n    doOld()\\\\n}\\\",\\n    new_string: \\\"func A() {\\\\n    doNew()\\\\n}\\\",\\n  },\\n  {\\n    // Uses context that still exists AFTER the first replacement\\n    old_string: \\\"func B() {\\\\n    callA()\\\\n}\\\",\\n    new_string: \\\"func B() {\\\\n    callA()\\\\n    logChange()\\\\n}\\\",\\n  },\\n]\\n```\\n\\n❌ Incorrect: Second old_string no longer matches due to whitespace change introduced by the first edit\\n\\n```\\nedits: [\\n  {\\n    old_string: \\\"func A() {\\\\n    doOld()\\\\n}\\\",\\n    new_string: \\\"func A() {\\\\n\\\\n    doNew()\\\\n}\\\", // Added extra blank line\\n  },\\n  {\\n    old_string: \\\"func A() {\\\\n    doNew()\\\\n}\\\", // Missing the new blank line, will FAIL\\n    new_string: \\\"func A() {\\\\n    doNew()\\\\n    logChange()\\\\n}\\\",\\n  },\\n]\\n```\\n\\n✅ Correct: Handling partial success\\n\\n```\\n// If edit 2 fails, edit 1 is still applied\\n// Response will indicate:\\n// - edits_applied: 1\\n// - edits_failed: [{index: 2, error: \\\"...\\\", edit: {...}}]\\n// You can then retry edit 2 with corrected context\\n```\\n</examples>\\n\",\"parameters\":{\"properties\":{\"edits\":{\"description\":\"Array of edit operations to perform sequentially on the file\",\"items\":{\"properties\":{\"new_string\":{\"description\":\"The text to replace it with\",\"type\":\"string\"},\"old_string\":{\"description\":\"The text to replace\",\"type\":\"string\"},\"replace_all\":{\"description\":\"Replace all occurrences of old_string (default false).\",\"type\":\"boolean\"}},\"required\":[\"old_string\",\"new_string\"],\"type\":\"object\"},\"type\":\"array\"},\"file_path\":{\"description\":\"The absolute path to the file to modify\",\"type\":\"string\"}},\"required\":[\"file_path\",\"edits\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"fetch\",\"strict\":false,\"description\":\"Fetches raw content from URL and returns it in specified format without any AI processing.\\n\\n<when_to_use>\\nUse this tool when you need:\\n- Raw, unprocessed content from a URL\\n- Direct access to API responses or JSON data\\n- HTML/text/markdown content without interpretation\\n- Simple, fast content retrieval without analysis\\n- To save tokens by avoiding AI processing\\n\\nDO NOT use this tool when you need to:\\n- Extract specific information from a webpage (use agentic_fetch instead)\\n- Answer questions about web content (use agentic_fetch instead)\\n- Analyze or summarize web pages (use agentic_fetch instead)\\n</when_to_use>\\n\\n<usage>\\n- Provide URL to fetch content from\\n- Specify desired output format (text, markdown, or html)\\n- Optional timeout for request\\n</usage>\\n\\n<features>\\n- Supports three output formats: text, markdown, html\\n- Auto-handles HTTP redirects\\n- Fast and lightweight - no AI processing\\n- Sets reasonable timeouts to prevent hanging\\n- Validates input parameters before requests\\n</features>\\n\\n<limitations>\\n- Max response size: 5MB\\n- Only supports HTTP and HTTPS protocols\\n- Cannot handle authentication or cookies\\n- Some websites may block automated requests\\n- Returns raw content only - no analysis or extraction\\n</limitations>\\n\\n<tips>\\n- Use text format for plain text content or simple API responses\\n- Use markdown format for content that should be rendered with formatting\\n- Use html format when you need raw HTML structure\\n- Set appropriate timeouts for potentially slow websites\\n- If the user asks to analyze or extract from a page, use agentic_fetch instead\\n</tips>\\n\",\"parameters\":{\"properties\":{\"format\":{\"description\":\"The format to return the content in (text, markdown, or html)\",\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds (max 120)\",\"type\":\"integer\"},\"url\":{\"description\":\"The URL to fetch content from\",\"type\":\"string\"}},\"required\":[\"url\",\"format\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"glob\",\"strict\":false,\"description\":\"Fast file pattern matching tool that finds files by name/pattern, returning paths sorted by modification time (newest first).\\n\\n<usage>\\n- Provide glob pattern to match against file paths\\n- Optional starting directory (defaults to current working directory)\\n- Results sorted with most recently modified files first\\n</usage>\\n\\n<pattern_syntax>\\n- '\\\\*' matches any sequence of non-separator characters\\n- '\\\\*\\\\*' matches any sequence including separators\\n- '?' matches any single non-separator character\\n- '[...]' matches any character in brackets\\n- '[!...]' matches any character not in brackets\\n</pattern_syntax>\\n\\n<examples>\\n- '*.js' - JavaScript files in current directory\\n- '**/*.js' - JavaScript files in any subdirectory\\n- 'src/**/*.{ts,tsx}' - TypeScript files in src directory\\n- '*.{html,css,js}' - HTML, CSS, and JS files\\n</examples>\\n\\n<limitations>\\n- Results limited to 100 files (newest first)\\n- Does not search file contents (use Grep for that)\\n- Hidden files (starting with '.') skipped\\n</limitations>\\n\\n<cross_platform>\\n- Path separators handled automatically (/ and \\\\ work)\\n- Uses ripgrep (rg) if available, otherwise Go implementation\\n- Patterns should use forward slashes (/) for compatibility\\n</cross_platform>\\n\\n<tips>\\n- Combine with Grep: find files with Glob, search contents with Grep\\n- For iterative exploration requiring multiple searches, consider Agent tool\\n- Check if results truncated and refine pattern if needed\\n</tips>\\n\",\"parameters\":{\"properties\":{\"path\":{\"description\":\"The directory to search in. Defaults to the current working directory.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The glob pattern to match files against\",\"type\":\"string\"}},\"required\":[\"pattern\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"grep\",\"strict\":false,\"description\":\"Fast content search tool that finds files containing specific text/patterns, returning matching paths sorted by modification time (newest first).\\n\\n<usage>\\n- Provide regex pattern to search within file contents\\n- Set literal_text=true for exact text with special characters (recommended for non-regex users)\\n- Optional starting directory (defaults to current working directory)\\n- Optional include pattern to filter which files to search\\n- Results sorted with most recently modified files first\\n</usage>\\n\\n<regex_syntax>\\nWhen literal_text=false (supports standard regex):\\n\\n- 'function' searches for literal text \\\"function\\\"\\n- 'log\\\\..\\\\*Error' finds text starting with \\\"log.\\\" and ending with \\\"Error\\\"\\n- 'import\\\\s+.\\\\*\\\\s+from' finds import statements in JavaScript/TypeScript\\n</regex_syntax>\\n\\n<include_patterns>\\n- '\\\\*.js' - Only search JavaScript files\\n- '\\\\*.{ts,tsx}' - Only search TypeScript files\\n- '\\\\*.go' - Only search Go files\\n</include_patterns>\\n\\n<limitations>\\n- Results limited to 100 files (newest first)\\n- Performance depends on number of files searched\\n- Very large binary files may be skipped\\n- Hidden files (starting with '.') skipped\\n</limitations>\\n\\n<ignore_support>\\n- Respects .gitignore patterns to skip ignored files/directories\\n- Respects .crushignore patterns for additional ignore rules\\n- Both ignore files auto-detected in search root directory\\n</ignore_support>\\n\\n<cross_platform>\\n- Uses ripgrep (rg) if available for better performance\\n- Falls back to Go implementation if ripgrep unavailable\\n- File paths normalized automatically for compatibility\\n</cross_platform>\\n\\n<tips>\\n- For faster searches: use Glob to find relevant files first, then Grep\\n- For iterative exploration requiring multiple searches, consider Agent tool\\n- Check if results truncated and refine search pattern if needed\\n- Use literal_text=true for exact text with special characters (dots, parentheses, etc.)\\n</tips>\\n\",\"parameters\":{\"properties\":{\"include\":{\"description\":\"File pattern to include in the search (e.g. \\\"*.js\\\", \\\"*.{ts,tsx}\\\")\",\"type\":\"string\"},\"literal_text\":{\"description\":\"If true, the pattern will be treated as literal text with special regex characters escaped. Default is false.\",\"type\":\"boolean\"},\"path\":{\"description\":\"The directory to search in. Defaults to the current working directory.\",\"type\":\"string\"},\"pattern\":{\"description\":\"The regex pattern to search for in file contents\",\"type\":\"string\"}},\"required\":[\"pattern\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"ls\",\"strict\":false,\"description\":\"Shows files and subdirectories in tree structure for exploring project organization.\\n\\n<usage>\\n- Provide path to list (defaults to current working directory)\\n- Optional glob patterns to ignore\\n- Results displayed in tree structure\\n</usage>\\n\\n<features>\\n- Hierarchical view of files and directories\\n- Auto-skips hidden files/directories (starting with '.')\\n- Skips common system directories like __pycache__\\n- Can filter files matching specific patterns\\n</features>\\n\\n<limitations>\\n- Results limited to 1000 files\\n- Large directories truncated\\n- No file sizes or permissions shown\\n- Cannot recursively list all directories in large projects\\n</limitations>\\n\\n<cross_platform>\\n- Hidden file detection uses Unix convention (files starting with '.')\\n- Windows hidden files (with hidden attribute) not auto-skipped\\n- Common Windows directories (System32, Program Files) not in default ignore\\n- Path separators handled automatically (/ and \\\\ work)\\n</cross_platform>\\n\\n<tips>\\n- Use Glob for finding files by name patterns instead of browsing\\n- Use Grep for searching file contents\\n- Combine with other tools for effective exploration\\n</tips>\\n\",\"parameters\":{\"properties\":{\"depth\":{\"description\":\"The maximum depth to traverse\",\"type\":\"integer\"},\"ignore\":{\"description\":\"List of glob patterns to ignore\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"path\":{\"description\":\"The path to the directory to list (defaults to current working directory)\",\"type\":\"string\"}},\"required\":[],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"sourcegraph\",\"strict\":false,\"description\":\"Search code across public repositories using Sourcegraph's GraphQL API.\\n\\n<usage>\\n- Provide search query using Sourcegraph syntax\\n- Optional result count (default: 10, max: 20)\\n- Optional timeout for request\\n</usage>\\n\\n<basic_syntax>\\n- \\\"fmt.Println\\\" - exact matches\\n- \\\"file:.go fmt.Println\\\" - limit to Go files\\n- \\\"repo:^github\\\\.com/golang/go$ fmt.Println\\\" - specific repos\\n- \\\"lang:go fmt.Println\\\" - limit to Go code\\n- \\\"fmt.Println AND log.Fatal\\\" - combined terms\\n- \\\"fmt\\\\.(Print|Printf|Println)\\\" - regex patterns\\n- \\\"\\\\\\\"exact phrase\\\\\\\"\\\" - exact phrase matching\\n- \\\"-file:test\\\" or \\\"-repo:forks\\\" - exclude matches\\n</basic_syntax>\\n\\n<key_filters>\\nRepository: repo:name, repo:^exact$, repo:org/repo@branch, -repo:exclude, fork:yes, archived:yes, visibility:public\\nFile: file:\\\\.js$, file:internal/, -file:test, file:has.content(text)\\nContent: content:\\\"exact\\\", -content:\\\"unwanted\\\", case:yes\\nType: type:symbol, type:file, type:path, type:diff, type:commit\\nTime: after:\\\"1 month ago\\\", before:\\\"2023-01-01\\\", author:name, message:\\\"fix\\\"\\nResult: select:repo, select:file, select:content, count:100, timeout:30s\\n</key_filters>\\n\\n<examples>\\n- \\\"file:.go context.WithTimeout\\\" - Go code using context.WithTimeout\\n- \\\"lang:typescript useState type:symbol\\\" - TypeScript React useState hooks\\n- \\\"repo:^github\\\\.com/kubernetes/kubernetes$ pod list type:file\\\" - Kubernetes pod files\\n- \\\"file:Dockerfile (alpine OR ubuntu) -content:alpine:latest\\\" - Dockerfiles with base images\\n</examples>\\n\\n<boolean_operators>\\n- \\\"term1 AND term2\\\" - both terms\\n- \\\"term1 OR term2\\\" - either term\\n- \\\"term1 NOT term2\\\" - term1 but not term2\\n- \\\"term1 and (term2 or term3)\\\" - grouping with parentheses\\n</boolean_operators>\\n\\n<limitations>\\n- Only searches public repositories\\n- Rate limits may apply\\n- Complex queries take longer\\n- Max 20 results per query\\n</limitations>\\n\\n<tips>\\n- Use specific file extensions to narrow results\\n- Add repo: filters for targeted searches\\n- Use type:symbol for function/method definitions\\n- Use type:file to find relevant files\\n</tips>\\n\",\"parameters\":{\"properties\":{\"context_window\":{\"description\":\"The context around the match to return (default: 10 lines)\",\"type\":\"integer\"},\"count\":{\"description\":\"Optional number of results to return (default: 10, max: 20)\",\"type\":\"integer\"},\"query\":{\"description\":\"The Sourcegraph search query\",\"type\":\"string\"},\"timeout\":{\"description\":\"Optional timeout in seconds (max 120)\",\"type\":\"integer\"}},\"required\":[\"query\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"view\",\"strict\":false,\"description\":\"Reads and displays file contents with line numbers for examining code, logs, or text data.\\n\\n<usage>\\n- Provide file path to read\\n- Optional offset: start reading from specific line (0-based)\\n- Optional limit: control lines read (default 2000)\\n- Don't use for directories (use LS tool instead)\\n</usage>\\n\\n<features>\\n- Displays contents with line numbers\\n- Can read from any file position using offset\\n- Handles large files by limiting lines read\\n- Auto-truncates very long lines for display\\n- Suggests similar filenames when file not found\\n</features>\\n\\n<limitations>\\n- Max file size: 250KB\\n- Default limit: 2000 lines\\n- Lines >2000 chars truncated\\n- Cannot display binary files/images (identifies them)\\n</limitations>\\n\\n<cross_platform>\\n- Handles Windows (CRLF) and Unix (LF) line endings\\n- Works with forward slashes (/) and backslashes (\\\\)\\n- Auto-detects text encoding for common formats\\n</cross_platform>\\n\\n<tips>\\n- Use with Glob to find files first\\n- For code exploration: Grep to find relevant files, then View to examine\\n- For large files: use offset parameter for specific sections\\n</tips>\\n\",\"parameters\":{\"properties\":{\"file_path\":{\"description\":\"The path to the file to read\",\"type\":\"string\"},\"limit\":{\"description\":\"The number of lines to read (defaults to 2000)\",\"type\":\"integer\"},\"offset\":{\"description\":\"The line number to start reading from (0-based)\",\"type\":\"integer\"}},\"required\":[\"file_path\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"write\",\"strict\":false,\"description\":\"Creates or updates files in filesystem for saving/modifying text content.\\n\\n<usage>\\n- Provide file path to write\\n- Include content to write to file\\n- Tool creates necessary parent directories automatically\\n</usage>\\n\\n<features>\\n- Creates new files or overwrites existing ones\\n- Auto-creates parent directories if missing\\n- Checks if file modified since last read for safety\\n- Avoids unnecessary writes when content unchanged\\n</features>\\n\\n<limitations>\\n- Read file before writing to avoid conflicts\\n- Cannot append (rewrites entire file)\\n</limitations>\\n\\n<cross_platform>\\n- Use forward slashes (/) for compatibility\\n</cross_platform>\\n\\n<tips>\\n- Use View tool first to examine existing files before modifying\\n- Use LS tool to verify location when creating new files\\n- Combine with Glob/Grep to find and modify multiple files\\n- Include descriptive comments when changing existing code\\n</tips>\\n\",\"parameters\":{\"properties\":{\"content\":{\"description\":\"The content to write to the file\",\"type\":\"string\"},\"file_path\":{\"description\":\"The path to the file to write\",\"type\":\"string\"}},\"required\":[\"file_path\",\"content\"],\"type\":\"object\"}},\"type\":\"function\"}],\"stream\":true}"
    headers:
      Accept:
      - application/json
      Content-Type:
      - application/json
      User-Agent:
      - OpenAI/Go 2.7.1
    url: https://api.openai.com/v1/chat/completions
    method: POST
  response:
    proto: HTTP/2.0
    proto_major: 2
    proto_minor: 0
    content_length: -1
    body: |+
      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"jhWmjHTNzc"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Found"},"finish_reason":null}],"usage":null,"obfuscation":"MAcYfGP"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}],"usage":null,"obfuscation":"SzR81Cks6FJ"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"30"},"finish_reason":null}],"usage":null,"obfuscation":"T1ZbwvqEyN"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" results"},"finish_reason":null}],"usage":null,"obfuscation":"zjSe"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".\n"},"finish_reason":null}],"usage":null,"obfuscation":"IPYEmXcA9"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"PzfZeSgpFUa"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" github"},"finish_reason":null}],"usage":null,"obfuscation":"02XOA"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".com"},"finish_reason":null}],"usage":null,"obfuscation":"QWGEFEMD"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/y"},"finish_reason":null}],"usage":null,"obfuscation":"yfcL8OV9Be"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"inch"},"finish_reason":null}],"usage":null,"obfuscation":"dNJ1JAGu"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"eng"},"finish_reason":null}],"usage":null,"obfuscation":"VxzNnBasN"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ts"},"finish_reason":null}],"usage":null,"obfuscation":"7kaT1XSoUt"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ing"},"finish_reason":null}],"usage":null,"obfuscation":"2e6pzfW5J"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"hua"},"finish_reason":null}],"usage":null,"obfuscation":"VsenWGy9n"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/g"},"finish_reason":null}],"usage":null,"obfuscation":"aVFsl0BCCs"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"olang"},"finish_reason":null}],"usage":null,"obfuscation":"Bw8hepI"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-E"},"finish_reason":null}],"usage":null,"obfuscation":"TkWtffUAUB"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"os"},"finish_reason":null}],"usage":null,"obfuscation":"sDzPvgBESo"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-d"},"finish_reason":null}],"usage":null,"obfuscation":"6Hbzsg2Iuh"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"pos"},"finish_reason":null}],"usage":null,"obfuscation":"mdHLc1Rdk"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-E"},"finish_reason":null}],"usage":null,"obfuscation":"ethD2z00ew"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"thereum"},"finish_reason":null}],"usage":null,"obfuscation":"Heb3g"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/c"},"finish_reason":null}],"usage":null,"obfuscation":"WXNYH2BA8p"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"md"},"finish_reason":null}],"usage":null,"obfuscation":"MglRDP2pjY"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/"},"finish_reason":null}],"usage":null,"obfuscation":"YGVdCTqBZcU"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"wn"},"finish_reason":null}],"usage":null,"obfuscation":"gsskxE96VK"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ode"},"finish_reason":null}],"usage":null,"obfuscation":"JC98MNvjA"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/main"},"finish_reason":null}],"usage":null,"obfuscation":"Z1Ioxsp"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".go"},"finish_reason":null}],"usage":null,"obfuscation":"5I9l39zyr"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":"},"finish_reason":null}],"usage":null,"obfuscation":"uuJUWGrqbcB"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"116"},"finish_reason":null}],"usage":null,"obfuscation":"A1pVefOku"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"ur3EQ3liJf6"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"121"},"finish_reason":null}],"usage":null,"obfuscation":"a3SzE9yiA"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"sL4gTAwm4V"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"7eisNU0WncP"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" github"},"finish_reason":null}],"usage":null,"obfuscation":"Iwark"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".com"},"finish_reason":null}],"usage":null,"obfuscation":"IlZfAOzk"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/y"},"finish_reason":null}],"usage":null,"obfuscation":"CYywEE0yH7"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"inch"},"finish_reason":null}],"usage":null,"obfuscation":"sqIBbgLG"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"eng"},"finish_reason":null}],"usage":null,"obfuscation":"eH2YelrS2"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ts"},"finish_reason":null}],"usage":null,"obfuscation":"3TDQ8Dn2Pa"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ing"},"finish_reason":null}],"usage":null,"obfuscation":"Uv4parHjm"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"hua"},"finish_reason":null}],"usage":null,"obfuscation":"KWx3pIMpk"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/go"},"finish_reason":null}],"usage":null,"obfuscation":"jhAlqa1uN"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"W1bcRLObuht"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ethereum"},"finish_reason":null}],"usage":null,"obfuscation":"MJOK"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-ch"},"finish_reason":null}],"usage":null,"obfuscation":"4saNiCOxc"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"inese"},"finish_reason":null}],"usage":null,"obfuscation":"tVIGjeS"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/c"},"finish_reason":null}],"usage":null,"obfuscation":"Y0RywlbkrV"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"md"},"finish_reason":null}],"usage":null,"obfuscation":"yMgqYP4V4H"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/"},"finish_reason":null}],"usage":null,"obfuscation":"KYjJ0w1JfGI"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"wn"},"finish_reason":null}],"usage":null,"obfuscation":"gUei3x5S1H"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"ode"},"finish_reason":null}],"usage":null,"obfuscation":"v9Nk58GVe"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/main"},"finish_reason":null}],"usage":null,"obfuscation":"Ue7OhoN"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".go"},"finish_reason":null}],"usage":null,"obfuscation":"czjzgVwXT"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":"},"finish_reason":null}],"usage":null,"obfuscation":"VazTzVRjrih"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"116"},"finish_reason":null}],"usage":null,"obfuscation":"HDyeUNjB4"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"jQ2puXDSHa2"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"121"},"finish_reason":null}],"usage":null,"obfuscation":"yYch4yObB"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\n"},"finish_reason":null}],"usage":null,"obfuscation":"Dqumk8Q8Sv"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"NwdpYLWsVrU"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":" github"},"finish_reason":null}],"usage":null,"obfuscation":"3dMIp"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".com"},"finish_reason":null}],"usage":null,"obfuscation":"5KWZFN8I"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/open"},"finish_reason":null}],"usage":null,"obfuscation":"b0cg8l4"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"containers"},"finish_reason":null}],"usage":null,"obfuscation":"bN"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/"},"finish_reason":null}],"usage":null,"obfuscation":"5RATrQrPBCt"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"um"},"finish_reason":null}],"usage":null,"obfuscation":"1t1Y998rrV"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"oci"},"finish_reason":null}],"usage":null,"obfuscation":"pP1h03BK1"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/c"},"finish_reason":null}],"usage":null,"obfuscation":"lizXtFY5k1"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"md"},"finish_reason":null}],"usage":null,"obfuscation":"KmftilsgNu"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/"},"finish_reason":null}],"usage":null,"obfuscation":"H22u8mlqrMc"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"um"},"finish_reason":null}],"usage":null,"obfuscation":"M0YXNrXqh6"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"oci"},"finish_reason":null}],"usage":null,"obfuscation":"gepvr1QPn"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"/main"},"finish_reason":null}],"usage":null,"obfuscation":"l6t1W8Y"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":".go"},"finish_reason":null}],"usage":null,"obfuscation":"nZILUibA3"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":":"},"finish_reason":null}],"usage":null,"obfuscation":"WXgDrIfl7G2"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"196"},"finish_reason":null}],"usage":null,"obfuscation":"qalMx2FCz"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"-"},"finish_reason":null}],"usage":null,"obfuscation":"JJ6DDD1LrAA"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"201"},"finish_reason":null}],"usage":null,"obfuscation":"BOLoqd76I"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"CCBlKQ"}

      data: {"id":"chatcmpl-CbUmHVSzjZaTP4HJ4h80gTMVIGdyI","object":"chat.completion.chunk","created":1763052481,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":12275,"completion_tokens":1048,"total_tokens":13323,"prompt_tokens_details":{"cached_tokens":10240,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":960,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"4KX1TabRal7bZ"}

      data: [DONE]

    headers:
      Content-Type:
      - text/event-stream; charset=utf-8
    status: 200 OK
    code: 200
    duration: 17.6626225s
